JVM - GC回收算法

说明

 

目录

  标记/清除算法

  复制算法

  标记/整理算法

  分代收集

  问题解答以及总结

标记/清除算法 

  1 介绍 首先标记出所有需要回收的对象,在标记完成后统一回收所有标记的对象

扫描二维码关注公众号,回复: 1698452 查看本文章

    1.1 标记阶段 对从GCRoot对象可达的对象都打上一个标志,一般在对象头中,将其记录为可达对象

    1.2 清除阶段 对堆内存进行遍历,如果发现某个对象没有被标记为可达对象(读取对象头),则将其回收

  2 注意 进行GC时,必须停止所有的java执行线程,在标记阶段进行可达性分析时,不可以出现分析过程中对象引用关系还在不断变化的情况,否则的话可达性分析结果的准确性就无法得到保证

  3 缺点 

    3.1 效率问题 标记和清除阶段两个阶段的效率都不高,需要遍历内存中的对象,很多时候内存中的对象实例数量是非常庞大的,很耗费时间,而且GC时需停止应用程序,会导致非常差的用户体验  

    3.2 空间问题 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾回收算法

复制算法

  1 解决问题 解决标记/清除算法(效率问题) 每次都是对整个半区进行内存回收

  2 原理 将可用内存按容量划分为大小相等的两块,每次使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另一块内存上,然后把这一块内存所有的对象一次性清理

  3 缺点

    3.1 将内存缩小为原来的一半,浪费了一半的内存空间,代价太高

    3.2 如果对象的存活率很高,那么我们需要将所有存活的对象复制一遍,耗费的时间代价较高

  4 注意 由于新生代中的几乎是"朝生夕死",都采用复制算法回收新生代(8:1:1)

标记/整理算法 

  1 原理

    1.1 标记阶段 (与标记/清除算法第一阶段一致)

    1.2 整理阶段 将所有的存活对象压缩到内存的一段,之后清除边界所有的空间

  2 整理顺序

    2.1 任意顺序 对象的而移动方式和它们初始的对象排列及引用关系无关

    2.2 线型顺序 将具有关联关系的对象排列在一起

    2.3 活动顺序 将对象"活动"到堆的一端,从而"挤出"垃圾,可以保持对象在堆中原有的顺序

  3 整理算法 (之后整理到算法目录)

分代收集算法(新生代追求速度,老年代追求空间)

  1 出现原因 根据对象的存活周期不同将内存划分为新生代和老年待,存活周期短的为新生代,存活周期长的为老年代,这样就可以根据每块内存的特点采用最适当的收集算法

  2 空间划分以及使用的回收算法

    2.1 堆(新生代,老年代) 新生代占堆空间1/3 老年代占堆空间2/3  新生代分为(eden,from survivor,to survivor)(8:1:1)

    2.2 新生代,使用复制算法  老年代 "标记-整理" 或者"标记-整理算法"  

 

问题解答以及总结 

  1 对象的存活周期

    1.1 朝生夕灭的对象  某一个方法的局部变量,循环内存的临时变量等

    1.2 存活周期较长的对象 缓存对象,数据库连接对象,单例对象等等

    1.3 几乎不灭的对象 String池中的对象,加载过的类信息等等

  2 新生代为什么使用复制算法,老年代为什么不使用

    2.1 在新手代,每次垃圾收集时都发现有大批对象死去,只有少量存活

    2.2 在老年代,对象存活率高,没有额外的空间进行分配担保

  3 为什么新生代除了eden区后,还要划分survivor区 (假设没有survivor区,Eden区每进行一次MinorGC,存活的对象都会被送到老年代,老年代很快被填满,触发full GC)

    3.1 假设Survivor替代方案

      3.1.1 增加老年代空间

        3.1.1.1 优点 更多存活对象才能填满老年代,降低Full GC频率

        3.1.1.2 缺点 随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长

      3.1.2 减少老年代空间

        3.1.2.1 优点 full GC所需时间减少

        3.1.2.2 缺点 老年代很快被存活对象填满,full GC频率增加

    3.2 总结 存在意义就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历一定次数的新生代GC还能在新生代存活的对象,才会被送到老年代

  4 为什么设置两个Survivor,而不是1个,3个

    4.1 假设只有一个Scurvior区 触发一次新生代GC后,eden区的存活对象就会被移动到Survivor区,循环下次,下一次gc时,eden和survivor各有一些存活对象,如果此时把eden区的存货对象硬放到Survivor区,很明显两部分的对象所占用的内存是不连续的,也就导致了内存碎片化 (当出现内存碎片化后,如果程序需要给一个内存需求很大的对象分配内存时,将出现问题)

    4.2 设立两个Survivor区 触发一次新生代GC后,eden中的存货对象移动到第一块survivor(SO),eden被清空,等eden区再满了,就再触发一次gc,eden和s0的存活对象又被复制到第二块survivor(s1),s0和eden被清空,然后下一轮s0和s1交换角色,如此循环往复 (永远有一个s是空的,另一个非空的s无碎片)

  5 算法比较

    5.1 效率 复制算法 > 标记/整理算法 > 标记/清除算法

    5.2 内存整齐率 复制算法 = 标记/整理算法 > 标记/清除算法

    5.3 内存利用率 标记/整理算法 = 标记/清除算法 > 复制算法

          

 

 

猜你喜欢

转载自www.cnblogs.com/hpzhu/p/9125175.html