GC垃圾回收

1. 垃圾回收之标记算法

  • 可达性分析算法

    1. 通过判断对象的引用链是否可达来决定对象是否可以被回收

    2. 可作为GC Root的对象

      • 虚拟机栈中引用的对象(栈帧中的本地变量表)

      • 方法区中常量引用的对象

      • 方法区中的静态属性引用的对象

      • 本地方法栈中JNI(Native方法)的引用对象

      • 活跃线程的引用对象

  • 引用计数算法

    • 优点:执行效率高,程序执行受影响较小

    • 缺点:无法检测出循环引用的情况,导致内存泄漏

2. 垃圾回收之回收算法

  • 标记-清除算法

    从根集合进行扫描,对存活的对象进行标记;对堆内存从头到尾进行线性遍历,回收不可达对象内存

    缺点:产生大量不连续的碎片化内存

  • 复制算法

    分为对象面和空闲面,对象在对象面上创建,存活的对象被从对象面复制到空闲面,最后将对象面所有对象内存清除

    优点:解决了碎片化问题,顺序分配内存,简单高效,适用于对象存活率低的场景(新生代适用)

  • 标记-整理算法

    从根集合进行扫描,对存活的对象进行标记;移动所有存活的对象,且按照内存地址依次排列,然后将末端内存地址以后的内存全部回收

    优点:避免内存的不连续性,不用设置两块内存互换,适用于存活率高的场景(老年代适用)

  • 分代收集算法

    按照对象生命周期的不同划分区域以采用不同的垃圾回收算法(垃圾回收算法的组合拳)

3. GC的分类

  • Minor GC

    新生代:尽可能快速收集掉那些生命周期短的对象,采用复制算法,包括Eden区和2个Survivor区,即S0区和S1区,其比例为8:1:1,新生代和老年代的比例是1:2

    image-20210526174325652

    • 对象如何晋级到老年代

      1. 新生代每经过一次Minor GC垃圾回收后,其存活的对象的年龄就增加1岁,默认达到15岁后该对象就会进入到老年代,也可以通过调整参数-XX:MaxTenuringThreshold来设置进入老年代的阈值。

      2. 经过多次的Minor GC后Survivor区中存放不下的对象

      3. 一个对象所占的内存超过了Eden区和Survivor区,该对象会直接晋级到老年代,可以通过参数-XX:PretenuerSizeThreshold设置最大对象的内存,超过该值就会直接进入老年代

    • 常用调优参数

      • -XX:SurvivorRatio Eden和Survivor的比值,默认是8,也就是Eden占新生代的8/10

      • -XX:NewRatio 老年代和新生代内存的大小的比例

      • -XX:MaxTenuringThreshold 对象从新生代晋升到老年代经过GC次数的最大阈值

  • Full GC

    老年代:生命周期存活较长的对象,一般采用标记-清除算法和标记-整理算法,一般使用Full GC,执行Full GC的同时也伴随着对新生代的垃圾回收

    Full GC 比Minor GC慢,但执行频率低

    • 触发Full GC的条件

      1. 老年代空间不足,一个在Eden区存放不下的大对象,会直接放到老年代,如果老年代也存放不下,就会触发Full GC

      2. 永久代空间不足(只针对KJDK7.0之前版本)

      3. CMS GC时出现promotion failed,concurrent mode failure

      4. Minor GC晋升到老年代的平均大小大于老年代的剩余空间

      5. 调用System.gc()

      6. 使用RMI来进行RPC或管理JDK应用,每小时执行一次Full GC

4. 常见的垃圾回收器

  1. 垃圾回收器之间的联系

    image-20210526184012384

    1.两个收集器之间有连线,表明它们可以搭配使用。

    2.其中Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案。

    3.(红色虚线)由于维护和兼容性测试的成本,再JDK8时将Serial+CMS、ParNew+Serial Old这两个组合声明为废弃,并在JDK9完全取消了这些组合的支 持,即移除。

    4.(绿色虚线)JDK14中,弃用Parallel Scavenge+SerialOld组合。删除CMS垃圾回收器。

  2. 年轻代常见的垃圾回收器

    1. Serial 收集器: -XX:+UseSerialGC来开启S该 收集器,采用复制算法

      • 单线程收集,进行垃圾回收时,必须暂停所有工作线程

      • 简单高效,Client模式下默认的年轻代收集器

    2. ParNew收集器:-XX:+UseParNewGC来开启该收集器,采用复制算法

      • 多线程收集,其余的行为、特点和Serial收集器一样

      • 单核执行效率不如Serial,在多核下执行才有优势

    3. Parallel Scavenge收集器:-XX:+UseParallelGC来开启该收集器,采用复制算法

      • 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如运行了100分钟,其中垃圾回收用了2分钟,则吞吐量=98%

      • 比起关注用户线程停顿时间(提升用户体验),更关注系统的吞吐量(适合在后台运算)

      • 在多核下执行才有优势,Server模式下默认的年轻代收集器

      • 对垃圾回收原理不太了解,以至于在优化过程中遇到问题时,可以在项目启动时设置参数-XX:+UseAdaptiveSizePolicy把内存调优交个JVM来完成

    4. 老年代常见的收集器

      1. Serial Old收集器:-XX:+UseSerialOldGC来开启该收集器,采用标记-整理算法

        • 单线程收集,垃圾收集时,必须暂停所有工作线程

        • 简单高效,Client模式下默认的老年代收集器

      2. Parallel Old收集器:-XX:+UseParallelOldGC来开启该收集器,采用标记-整理算法

        • 多线程,吞吐量优先

      3. CMS收集器:-XX:+UseConcMarkSweepGC,标记-清除算法

      4. G1收集器:-XX:+UseG1GC,复制+标记-整理算法(Garbage First)

        • 并发和并行

        • 分代收集

        • 空间整合

        • 可预测的停顿

        •  

     

 

 

原文地址:https://www.cnblogs.com/seanRay/p/14814821.html