聊聊ART虚拟机_对象的使用和销毁问题

简介: 在上一篇文章中,我们聊到了对象的分配问题,简单说明了何为 ART 虚拟机,以及对象中类的加载、内存布局等问题。本文继接上文,将会讲完对象的使用和销毁的问题,希望本文对你有所帮助。

前置知识

前言

在上一篇文章中,我们聊到了对象的分配问题,简单说明了何为 ART 虚拟机,以及对象中类的加载、内存布局等问题。本文继接上文,将会讲完对象的使用和销毁的问题,希望本文对你有所帮助。

在 ART 中,对于一个对象,我们可以从其分配、使用到销毁来看,其主要结构如下:

1.webp.jpg内存分配

分配器

在 Android 里面的内存分配是由内存分配器来进行分配的。其作用如下:

APP 的 java 对象内存分配是托管给 VM 来处理的,不会直接向操作系统申请。VM 就像一个连接 java 代码和内存的中介,VM 才是真正的内存管控者,其控制对内存的占用和内存布局。

1.webp.jpg

而分配器则分为 3 类分配器,分别是:TLABROSallocatorLOSallocator。他们的原理和作用如下:

  • 小内存/临时变量 -> TLAB:给每个线程开一些小缓存,一些小的对象,例如栈上的对象就在 TLAB 上面进行分配。
  • 中等内存/数组、容器 -> ROSallocator:而较为大些的对象,则在 ROSallocator 中进行分配,这时候 ROSallocator 会直接去使用 VM mem pool ,此为 ART 托管的内存池。
  • 大量内存/Bitmap存储图片 -> LOSallocator:更大的对象,交由 LOSallocator 去处理,就直接从 linux 上面取。例如 Bitmap 就是这样子。

1.webp.jpg

内存碎片

由于内存中分配算法的问题,出现了多次分配后使得找不出一块连续的内存来放置当前需要的内存块,从而导致内存溢出。而 ART 分配原理就是找到一段在最优范围里面符合大小的连续内存

如下图中所示,未被使用的内存很分散,但是却由于不能组成连续的内存块,无法被使用了。这些分散的内存块就是内存碎片。而这些内存碎片就是由内存分配过程中导致的,事实上,我们可以使用较好的内存回收策略来解决这些碎片问题。

1.webp.jpg

内存回收

当出现了内存碎片问题可以用两种方法来解决。分别是 GC 和 RC。

GC:垃圾回收 (Garbage Collection),是一些语言管理内存的方式,如 Java 语言等;程序员不需要主动管理内存,程序运行时环境(虚拟机)会做垃圾回收的工作,就是在合适的时机 自动释放不再需要的内存。(需要系统去主动收集不使用的对象)

RC:引用计数(Reference Count),每当有一个新的强引用指针指向,对象的引用计数就会+1,当减少一个强引用指针,引用计数就会-1,当引用计数为0时,对象就会被销毁。在 iOS 的 Swift 中,对象的内存是通过引用计数来管理的。(无需系统主动去收集不使用的对象)

RC的问题以及解决方案

事实上,虽然 RC 释放内存十分及时,但是单纯使用 RC 机制会出现一些问题的。这个问题就是环引用问题。

当两者出现环应用的话,就很难对其进行回收,因为他们之间会出现一个死结,导致其引用计数不会小于1。

1.webp.jpg

为解决环引用的问题,IOS 的策略是使用弱引用和手动标记。手动标记中是将解环动作移交给了开发者,让其能小于1。而弱引用则是这样子,当对象有其他的对象引用的时候,弱引用就算作一种引用,可以找到被引用对象;若是没有其他人引用,只有相互引用的时候,弱引用就不算引用。

1.webp.jpg

ART的引用

而在 ART 中,其使用的是 GC 机制,但是同时它也具有三种引用机制。

  • 强引用:直接持有的,无法被 GC 回收
  • 软引用:内存不足时候会被回收
  • 弱引用:一旦触发 GC 必定被回收

我们需要注意的是:GC 并非在内存不够的时候才会被触发

触发GC的条件

如果 GC 只是在内存不够的时候才被触发,那么就会导致很多的问题。如果在内存不够才被触发,那么会使得性能下降且 GC 裂化。

而在 Android 中,触发 GC 的条件有如下两个

  1. 内存不够了
  2. 手机认为该 GC 了
  • VM 堆占用达到水位。该水位是维持性能和内存的平衡点。
  • 系统内存紧张。进程太多了,需要 GC 一下。
  • 未知原因。可能是锁屏了,有些手机认为你在锁屏后不会再次使用,然后就趁机 GC 一下。

1.webp.jpg

由此我们可以看出,IOS 的内存回收是要比 Android 要及时的,所以 Android 的内存中总是会存有垃圾在的。这也是为何 IOS 内存少,但是运行却未受太大影响的原因。

GC的方式

首先,我们需要了解一下 GC Root。GC Root 就是 GC 的起点,从这个起点出发的对象,都不能被释放。因为GC 认为 GC Root 以及它引用的对象是程序后面的可能会用到的,所以不会释放;没有被 GC Root 直接或间接引用的对象,后面一定不会被用到,可以被释放掉。

一般有如下四个起点:

  • 栈/在栈内存的变量
  • static 变量
  • native 中的 JNI 引用的对象(native ref)
  • VM 保留

如下图所示,处理 对象C ,其他的对象其引用起点都是 GC Root,所以只有 对象C 需要被 GC 掉。

1.webp.jpg

而在基于 GC Root 来进行 GC 的方式则有两种,包括 Tracing GCCopy GC

Tracing GC

Tracing GC 的方法是 从 root 遍历,然后将遍历过的都标记下来,被标记过的就是 GC Root 引用链上面的,最后将没有标记的 GC 掉即可。

1.webp.jpg

Copy GC

Copy GC 的方法也是 从 root 出发,不过是将遍历过了对象 copy 到一个空闲的区域,然后将原有的区域的内存集中 GC 掉。这个方法的好处就是可以获得一块连续的内存空间。

1.webp.jpg

ART中的应用场景

而在 ART 中,对于两种 GC 是的应用场景是如下这样子的:

前台GC 后台GC
使用场景 应用在前台的进程 非前台应用进程、service/push进程
算法 Tracing GC(mark-sweep) Tracing GC(compacting)
速度
内存碎片
额外空间 不需要 需要

如何编写内存友好的代码

基于上文的讲述,我们知道了内存的分配和回收的机制,那么我们写代码的时候应该考虑我们的代码是否对内存是友好的。例如我们在考虑使用数组或者是链表的时候,除了其增删查改性能的考虑,我们仍需考虑到其是否对内存友好。例如数组是连续的,对内存友好,而链表是离散的,对内存不友好;那么在数量基本确定的时候,我们应该首要使用数组结构。

并且,我们需要了解到 Finalizer 机制和 Cleaner 机制的不同。Finalizer 的在生命周期中只会执行一次,再次被激活后就不会再次触发这个机制了,而如果我们对其二次激活做判断和保留的话,又容易导致内存泄露问题。所以说,我们需要尽量使用 Cleaner 机制。

1.webp.jpg



相关文章
|
8月前
|
存储 安全 Java
JavaSE高级篇:HotSpot虚拟机对象探秘
JavaSE高级篇:HotSpot虚拟机对象探秘
|
存储 缓存 算法
JVM第三讲:深入理解java虚拟机之垃圾回收算法?CMS垃圾回收的基本流程?对象引用类型?
JVM第三讲:深入理解java虚拟机之垃圾回收算法?CMS垃圾回收的基本流程?对象引用类型?
255 0
|
3月前
|
存储 缓存 Java
深度解密 Python 虚拟机的执行环境:栈帧对象
深度解密 Python 虚拟机的执行环境:栈帧对象
82 13
|
3月前
|
存储 Python
解密虚拟机的执行环境:栈帧对象
解密虚拟机的执行环境:栈帧对象
33 0
|
7月前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
78 3
|
8月前
|
存储 缓存 算法
深入浅出JVM(一)之Hotspot虚拟机中的对象
深入浅出JVM(一)之Hotspot虚拟机中的对象
|
存储 算法 安全
JVM:HotSpot虚拟机----对象的创建简单介绍及对象内存布局详解
JVM:HotSpot虚拟机----对象的创建简单介绍及对象内存布局详解
208 0
JVM:HotSpot虚拟机----对象的创建简单介绍及对象内存布局详解
|
缓存 算法 Java
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
240 0
|
存储 缓存 算法
《深入理解Java虚拟机》读书笔记(二)--对象的创建与空间分配及定位
《深入理解Java虚拟机》读书笔记(二)--对象的创建与空间分配及定位
124 0
|
存储 算法 Java
HotSpot 虚拟机对象探秘
HotSpot 虚拟机对象探秘
131 0

热门文章

最新文章