美文网首页
Android 内存 - 垃圾回收(GC)机制

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

作者: Bfmall | 来源:发表于2021-03-26 16:48 被阅读0次

    虽然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

    回收原理:

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

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

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

    步骤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内存优化(大汇总——全)

    相关文章

      网友评论

          本文标题:Android 内存 - 垃圾回收(GC)机制

          本文链接:https://www.haomeiwen.com/subject/hftshltx.html