06-底层必备源码-JVM底层-GC算法流程(自我总结)

一 判断 是否GC 的算法

1.引用计数法:

1.1 规则:如果这个obj被引用,计数器+1,引用失效,计数器-1。当一个obj的引用计数器为0,就不代表被使用。

1.2 缺点:不能解决循环引用的问题

2.可达性分析算法

2.1 规则:以GCroot为起点,向下搜索,经过的链为引用链,当一个obj没有任意一个到GCroot的引用链,证明他可以被回收。

2.2 可作为GCroot的对象:

  • java虚拟栈的引用对象
  • 方法区的静态属性、常量引用的对象
  • 本地方法区中引用的对象

二 GC 的具体算法

1.标记-清除:首先标记出需要回收的对象,标记完成之后统一清除对象。

标记:从根集合开始扫描,标记存货的对象

清除:扫描整个堆内存空间,回收未被标记的对象,使用free-list记录可以使用的区域

优点:基于最基础的可达性分析算法,它是最基础的收集算法;而后续的收集算法都是基于这种思路并对其不足进行改进得到的;

缺点:

  • 效率问题:标记和清除都需要扫描,两个过程的效率都不高;
  • 空间问题:标记清除后会产生大量不连续的内存碎片,这会导致分配大内存对象时,无法找到足够的连续内存,从而需要提前触发另一次垃圾收集动作。
  • stop-the-Word:在标记时需要暂停JVM用户进程

2.标记-复制:它将可用内存容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完之后,就将还存活的对象复制到另外一块上面,然后在把已使用过的内存空间一次理掉。

JVM实现原理:Survivor区,一块叫From,一块叫To,对象存在Eden和From块。当进行GC时,Eden存活的对象全移到To块,而From中,存活的对象按年龄值确定去向,当达到一定值(年龄阈值,通过-XX:MaxTenuringThreshold可设置,默认=15)的对象会移到年老代中,没有达到值的复制到To区,然后直接清空Eden和From。之后,From和To交换角色,新的From即为原来的To块,新的To块即为原来的From块,且新的Form块中对象年龄加1.

优点:内存分配时也不用考虑内存碎片等问题;实现简单,运行高效;可以利用指针碰撞(bump-the-pointer)实现快速内存分配

缺点:

  • 空间浪费:可用内存缩减为原来的一半,太过浪费(解决:可以改良,不按1:1比例划分);
  • 效率随对象存活率升高而变低:当对象存活率较高时,需要进行较多复制操作(对象的引用地址需要复制),效率将会变低,所以该算法不适合对象存活率较高的场景或者区域。

应用场景:

  • 现在商业JVM都采用这种算法(通过改良缺点1)来回收新生代;
  • 如Serial收集器、ParNew收集器、Parallel Scavenge收集器、G1(从局部看)。

3. 标记-整理 算法

06-底层必备源码-JVM底层-GC算法流程(自我总结)_第1张图片

标记-整理:标记操作和“标记-清理”算法一致,后续操作不只是直接清理对象,而是在清理无用对象前,先将存活的对象都向一端移动,并更新引用其对象的指针,然后直接清理掉端边界以外的内存。

标记:和“标记-清理”算法一致

整理:扫描整个堆内存空间,将存活的对象都向一端移动,并更新引用其对象的指针,然后直接清理掉边界以外的内存。

整理的目的 :就是整合零散分布的空间碎片为一个连续的空间。

优点:

  • 不会像复制算法,效率随对象存活率升高而变低
  • 不会像标记-清除算法,产生内存碎片,因为清除前,进行了整理,存活对象都集中到空间一侧;

缺点:主要是效率问题:除像标记-清除算法的标记过程外,还多了需要整理的过程,效率更低;

应用场景:回收老年代;

4、标记-清除-整理(Mark-Sweep-Compact)

该算法是标记清除和标记整理的结合,

标记-清除会产生碎片,

标记-整理每次都进行整理效率不高;

标记-清楚-整理 是如果老年代内存中没有一块连续续的空间可以存放将要进入对象,就进行整理;

如果内存中的空间可以存放将要进入的对象,就进行标记-清除,这样就节省了整理的步骤可以提高效率。总结一句话:不是所有的时候都需要整理的,因为整理也付出代价。主要应用于老年代。

总结: 没有最好的算法,只有最合适的引用场景06-底层必备源码-JVM底层-GC算法流程(自我总结)_第2张图片

三 新生代、老年代对象存放原则

  • 1. 对象优先在eden区分配
  • 2.大对象直接放入老年代
  • 新生代GC回收算法采用的是复制算法;
    考虑到大对象可能的存活时间长,频繁复制带来额外开销;
    jvm通过设置参数值,当大于该值的对象直接放入老年代。
    123

  • 3.survivor区长期存活的对象移动到老年代
  • 新生代工作流程:
    1.对象放入eden区,直至eden区放不下该对象,发生youngGC
    2.Young GC流程是将Eden中有效的对象和survivor from区拷贝到 survivor to区
    3.清除eden区数据,将from和to指针交换
    ------
    survivor from中的部分数据存活时间可能过长,频繁复制带来额外开销,
    jvm通过利用age(每young gc交换一次 +1,默认15次)来判断是否将该对象移动到老年代,
    当对象的age==15时,将其移动到老年代
    12345678

  • 4.survivor区动态规划
  • 当survivor区发现有超过一半的对象age相同
    jvm会把这些对象移动到老年代
    12

  • 5.老年代担保原则
  • 当发生youngGC之前,jvm会检查老年代剩余空间是否大于当前新生代所有数据
    如果大于 直接顺利进行GC
    如果不大于,则判定这是一次不安全的GC,通过检查设置是否运行不安全GC,
    如果允许,则判断剩余空间是否大于历届youngGC平均值,如果是,直接进行youngGC
    如果不是,则对老年代进行full GC后,再进行YoungGC
    -----
    ps: 因为full GC的标记-整理算法耗时是youngGC的复制算法10倍以上
    
    12345678

    补充

  • YoungGC 又叫 MinorGC
  • 有种说法是对新生代进行垃圾回收叫做minor GC,对老年代进行垃圾回收叫做major GC,同时对新生代、老年代和永久代进行垃圾回收叫做full GC
  • 事实上有种说法是对新生代进行垃圾回收叫做minor GC,对老年代进行垃圾回收叫做major GC这个是对的,最后一句话存疑,我所参考的深入理解java虚拟机,对full GC的表述和majorGC相同,因为再进行MajorGC后必会发生一次YoungGC

你可能感兴趣的:(Java_P5,java)