Android 内存 - 垃圾回收(GC)机制

虽然Java大部分内存由垃圾回收器(GC)管理着,但是对内存的释放还是有必要知道一点的,毕竟对我们编程也是有一定的帮助的。例如,解决内存泄露问题等等!!

GC回收哪个区域的垃圾

这里所说的区域指的是运行时数据区,有5种:程序计数器,虚拟机栈,本地方法栈,堆,方法区。
我们一一分析哪个区域由GC管理:

  • 程序计数器:这里只保存当前线程锁执行的字节行号,也就是执行到了哪里,只是一块很小的内存,不在GC管理范围内。

  • 虚拟机栈:这里保存的是方法执行时创建的栈帧,方法执行完后,自动从虚拟机栈弹出。也就是说,栈内数据,超过作用域后会被自动释放(创建的对象在堆内,栈只是保存对象的引用),所以也不在GC管理范围内。

  • 本地方法栈:和虚拟机栈类似,只不过是Native方法,所以也不在GC管理范围内。

  • 堆:储存对象实例和数组,大部分垃圾都在这里产生,这里是GC重点管理的区域。

  • 方法区:储存类信息,常量,静态变量等信息,也会储存对象信息,也在GC管理范围内。

所以GC主要回收是的 方法区 的垃圾。

GC回收哪些对象(所谓的“垃圾”)

有2种方法判断对象是“垃圾”,可以被GC回收:

引用计数算法

给对象添加一个引用计数器,当一个地方引用该对象,计数器值+1;当引用失效时,计数器值-1;如果引用计数器的值为0,说明该对象不能再次使用,也就成了“垃圾”,可以被GC回收。

引用计数法不能解决循环引用问题,也就是A对象持有B,B对象持有A,那么A和B的计数器值永远不会为0。故Java没有采用这种算法管理内存。

根搜索算法

这种算法的思路是通过一系列名为“GC ROOT”的对象为起始点,从这个节点像下搜索,搜索走过的路径为引用链,当一个对象没有任何引用链相连,即从GC Root到该对象不可达,则证明该对象不可用,也就成了“垃圾”,可以被GC回收。

哪些可以作为“GC ROOT”

  • 虚拟机栈(栈帧中的本地变量表)中的引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中JNI(Native方法)的引用对象

GC什么时候执行

  • 新生代eden空间满了,触发minor(小) gc;
  • 升到老年代的对象大于老年代剩余空间,触发full(满) gc;
  • 或者没满,但是被HandlePromotionFailure参数强制了,触发full(满) gc;

GC做了什么

我们先了解分代的垃圾回收机制:

新生代(Young generation)

用于保存第一次创建的对象,分为3个区域:1个伊甸园空间和2个幸存者空间,默认的比例是8:1:1

回收原理:

  1. 绝大多数刚刚被创建的对象会存放在伊甸园空间(Eden)。

  2. 在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(如S1,S1为From区域),伊甸园空间会被清空。

  3. 再次伊甸园空间执行GC后(Minor GC 是对整个新生代检查,不仅仅是伊甸园空间),如果幸存者空间S1的对象依旧还存活,那么这些存活的对象年龄增加。然后所有还存活的对象会被移动到另一个幸存者空间(如S2,S2是下次Minor GC的From区域)中,伊甸园空间和幸存者空间S1会被清空。

  4. 步骤3执行N(N = MaxTenuringThreshold(年龄阀值设定,默认15))次之后,依然存活的对象,就会被移动到老年代。

一般来说,新生代对象要经历N次Minor GC之后才会被移动到老年代。不过也有例外,对于一些比较大的对象(需要分配一块比较大的连续内存空间),则直接进入到老年代。一般在Survivor 空间不足的情况下发生。

老年代(Old generation)

老年代只有一个区域,但是这块区域很大,默认大小是新生代的2倍,正因如此,发生在老年代的GC(发生在老年代的GC叫Full GC)次数要比新生代少得多,而且做一次 Full GC 的时间比 Minor GC 要更长(约10倍)。

回收老年代的算法有几种,待会再分析。

持久代(Permanent generation)

也称之为 方法区(Method area):用于保存类常量以及字符串常量。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Full GC 。

垃圾回收算法
标志-清除算法

从根集合(GC ROOT)开始扫描,第一次扫描,标志存活的对象,第二次扫描,直接回收未被标志的对象。

因为未对存活的对象做移动处理,仅对不存活的对象进行处理,所以效率较高,也因为如此,会残留很多内存碎片。

复制算法

复制算法把内存分为2个区间,一个为活动区间,一个为空闲区间。

从根集合(GC ROOT)开始扫描,把存活的对象拷贝到空闲区间,扫描完活动区间后,一次性回收整个区间的内存。此时,空闲区间就成了活动区间,如此反复。

复制算法以牺牲一半的内存为代价,换来高速率的回收,典型的以空间换时间!因此,复制算法适用于对象存活率较低的内存中。

到这里,你应该会想到新生代的垃圾回收原理吧?!正是,新生代垃圾回收采用的就是复制算法

标志-整理算法

标志-整理算法采用了标志-清除算法去回收不存活的对象,但在回收后,会将存活的对象往左端空闲空间移动,并更新对应的指针。所以相对于标志-清除算法,解决了标志-清除算法残留的内存碎片的问题,但因此成本会更高,比较适用回收不那么频繁的内存中。

由于年老代的Full GC不容易触发,所以年老代的垃圾回收采用了标志-整理算法

垃圾回收器

关于有哪些垃圾回收器,请参考 Java性能优化之JVM GC(垃圾回收机制) 的 “垃圾回收器简介”一节。

配置参数

-Xms JVM启动的时候设置初始堆的大小
-Xmx 设置最大堆的大小
-Xmn 设置年轻代的大小
-XX:PermSize 设置持久代的初始的大小
-XX:MaxPermSize 设置持久代的最大值

内存优化

内存优化可以参考5R做法:Reckon(计算),Reduce(减少) ,Reuse(重用),Recycle(回收),Review(检查)。

请参考:
ANDROID内存优化(大汇总——上)
ANDROID内存优化(大汇总——中)
ANDROID内存优化(大汇总——全)

参考资料

Java性能优化之JVM GC(垃圾回收机制)
Java gc(垃圾回收机制)小结,以及Android优化建议
ANDROID内存优化(大汇总——上)
ANDROID内存优化(大汇总——中)
ANDROID内存优化(大汇总——全)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值