《算法撕裂者》04 - 垃圾收集算法详解

五、垃圾收集(GC)算法

最近在学习 JVM 的知识,作为一个 Java 开发人员不懂 JVM 实在是不应该,毕竟底层基础决定上层建筑,而作为 JVM 里面的重点之一必然是垃圾收集(GC)了,那今天就带大家一起来了解下这是辣鸡,竟然如此的出名。(这篇也还是放在《算法撕裂者》系列吧,毕竟带"算法"两字,就水一波吧)

0、java对象内存申请过程

img

  1. JVM会试图为相关Java对象在Eden中初始化一块内存区域;当Eden空间足够时,内存申请结束。否则到下一步;
  2. JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
  3. Survivor区被用来作为Eden及old的中间交换区域,当old区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
  4. 当old区空间不够时,JVM会在old区进行major collection;
  5. 垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误";

1、stop the world

在新生代进行的GC叫做minor GC,在老年代进行的GC都叫major GC,Full GC同时作用于新生代和老年代。在垃圾回收过程中经常涉及到对对象的挪动(比如上文提到的对象在Survivor 0和Survivor 1之间的复制),进而导致需要对对象引用进行更新。为了保证引用更新的正确性,Java将暂停所有其他的线程,这种情况被称为“Stop-The-World”,导致系统全局停顿。Stop-The-World对系统性能存在影响,因此垃圾回收的一个原则是尽量减少“Stop-The-World”的时间。

不同垃圾收集器的Stop-The-World情况,Serial、Parallel和CMS收集器均存在不同程度的Stop-The-Word情况;而即便是最新的G1收集器也不例外。

  • Java 中一种全局暂停的现象,JVM挂起状态
  • 全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互
  • 多半由于JVM的GC引用,如:
    • 老年代空间不足
    • 永生代(jkd7)或者元数据空间(jkd8)不足。
    • System.gc()方法调用。
    • CMS GC时出现promotion failed和concurrent mode failure
    • YoungGC时晋升老年代的内存平均值大于老年代剩余空间
    • 有连续的大对象需要分配
  • 除了GC还有以下原因:
    • 死锁检查
    • Dump线程–人为因素
    • 堆Dump–人为因素

关于stop the world 可查看: https://www.jianshu.com/p/d686e108d15f

2、如何判断对象是否“死去”?

在堆里面存放着Java中几乎所有的对象实例,垃圾收集器在对堆进行回收之前,第一件事情就是要确定哪些对象还存活着,哪些对象已经死去(即不可能在被任何途径使用的对象)。

  • 引用计数算法
  • 可达性分析算法
a、引用计数算法

引用计数法顾名思义,就是对一个对象被引用的次数进行计数,当增加一个引用计数就加1,减少一个引用计数就减1。

image

图片来自 https://www.cnblogs.com/leefreeman/p/7389919.html

上图表示3个Teacher的引用指向堆中的Teacher对象,那么Teacher对象的引用计数就是3,以此类推Student对象的引用计数就是2。

image

上图表示Teacher对象的引用减少为2,Student对象的引用减少为0(减少的原因是该引用指向了null,例如teacher3=null),按照引用计数算法,Student对象的内存空间将被回收掉。

引用计数算法原理非常简单,是最原始的回收算法,但是java中没有使用这种算法,原因有2:

  1. 频繁的计数影响性能,

  2. 它无法处理【循环引用】的问题。

    例如Teacher对象中引用了Student对象,Student对象中又引用了Teacher对象,这种情况下,对象将永远无法被回收。

b、可达性分析算法

这个算法的基本思想就是,通过一系列称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为【引用链】(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说,就是从 GC Roots 到这个对象不可达)时,则证明此对象时不可用的。

1.jpg-40.4kB

在 Java 语言中,可作为 GC Roots 的对象包括一下几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI (即一般说是 Native 方法)引用的对象
c、再谈引用

在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为

  1. 强引用(Strong Reference)
  2. 软引用(Soft Reference)
  3. 弱引用(Weak Reference)
  4. 虚引用(Phantom Reference)
  • 强引用

    代码中普遍存在的,类似 “ Object obj = new Object() ” 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

  • 软引用

    用来描述一些还有用但非必须的对象。软引用所关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围,并进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。提供了 SoftReference 类实现软引用。

  • 弱引用

    描述非必须的对象。强度比软引用更弱一些,被弱引用关联的对象,只能**生存到下一次垃圾收集发生之前。**当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。提供了 WeakReference类实现弱引用。

  • 虚引用

    一个对象是否有虚引用,完全不会对其生存时间构成影响,也无法通过一个虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的,就是能在这个对象被收集器回收时收到一个系统通知。通过了 PhantomReference 类实现虚引用。

3、标记-清除算法(Mark-Sweep)

什么是标记-清除算法

分为【标记】和【清除】两个阶段。首先标记处所有需要回收的对象,在标记完成后统一回收被标记的对象。

有什么缺点?
  1. 效率问题:标记和清除的效率都不高
  2. 空间问题:标记清除后会产生大量**【不连续的内存碎片】,空间碎片太多可能导致,程序分配较大对象**时无法找到足够的连续内存,不得不提前出发另一次垃圾收集动作。

2.jpg-81.6kB

4、复制算法(Copying)- 新生代

将可用内存按容量划分为【大小相等的两块】,每次只使用其中一块。当这一块的内存用完了,就将存活着的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉。

优点?

复制算法使得每次都是针对其中的一块进行内存回收,内存分配时也不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

缺点?

将内存缩小为原来的一半。在对象存活率较高时,需要执行较多的【复制操作】,效率会变低。

3.jpg-95.7kB

应用?

商业的虚拟机都采用复制算法来回收新生代。因为新生代中的对象容易死亡,所以并不需要按照1:1的比例划分内存空间,而是将内存分为一块较大的 Eden 空间两块较小的 Survivor 空间。每次使用 Eden 和其中的一块 Survivor。

当回收时,将 Eden 和 Survivor 中还存活的对象一次性拷贝到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。**Hotspot 虚拟机默认 Eden 和 Survivor 的大小比例是8:1,**也就是每次新生代中可用内存空间为整个新生代容量的90%(80% + 10%),只有10%的内存是会被“浪费”的。

5、标记-整理算法(Mark-Compact)- 老年代

标记过程仍然与“标记-清除”算法一样,但不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。

img

优点?

经过整理(压缩),即再次扫描,并往一端**【滑动】**存活对象,没有内存碎片

缺点?

需要移动对象的成本

6、分代收集算法

根据对象的存活周期,将内存分为几块。一般是把【Java堆】分为【新生代】和【老年代】, 这样就可以根据各个年代的特点,采用最适当的收集算法。

  • 新生代:每次垃圾收集时会有大批对象死去,只有少量存活,所以选择【复制算法】,只需要少量存活对象的复制成本就可以完成收集。
  • 老年代:对象存活率较高,没有额外空间对它进行分配担保,必须使用【标记-清除】或【标记-整理】算法进行收集。

参考资料:

《深入理解Java虚拟机(第2版)》 周志明

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章