天天看点

老生常谈的java垃圾回收机制

一、确定被回收的对象

1. 引用计数(Reference Counting)

给对象添加一个引用计数器,当该对象被其他对象引用时,计数器加一;引用失效时计数器减一;引用数为0的对象就是需要被回收的对象。

这样的算法实现简单,但也存在弊端,比如有两个对象相互引用,但却没有其他任何地方引用它们,它们应当被视为“垃圾”,但他们的引用计数器并不为0;

2.可达性分析(Reachability Analysis)

选择一系列对象作为起始点(GC Roots),从这些节点开始进行DFS,搜索过的路径被称为引用链,若某个对象未处在任何引用链中,即该对象不可达,那么就认为这个对象是可被回收的;

GC Root可以是:

  • 虚拟机栈中引用的对象,即函数内局部变量所引用的对象;
  • 方法区中类静态属性引用的对象;
  • 方法区中常量引用的对象;
  • 本地方法栈中引用的对象;

3. 对象的finalize方法

当一个对象在某一次可达性分析中,被标记为不可达,那么它的finalize方法就可能被调用,前提是:

  • 该对象的finalize方法被覆写;
  • 该对象的finalize方法为被执行过;

若满足以上两个条件,该对象会被加入到F-Queue中,由一个Finalizer线程执行;

虚拟机并不保证队列中的对象的finalize方法一定会被执行,等到第二次标记到来时,仍然被标记为不可达的对象将会在不远的将来被回收;

不过在项目代码中,finalizer最好不要被重写;

4.方法区的回收

方法区的垃圾回收主要包括:废弃常量和无用的类;

废弃常量顾名思义是指没有被用到的常量,即当前进程中没有任何一个对象引用了该常量;

判定一个类是垃圾就比较麻烦:

  • 首先,堆上不存在任何该类的实例;
  • 该类的classloader被回收;
  • 类对应的对象没有被引用,无法通过反射调用该类的方法;

二、垃圾回收算法

1. 标记-清除(Mark-Sweep)

首先标记出所有需要被回收的对象,然后统一回收;

它的不足在于效率较低且会产生大量的内存碎片;

2. 复制算法(Copying)

将内存分为两块,每次只使用其中一块,进行回收时,将仍存活的对象复制到另半块内存中,然后清理已使用的半块内存;

优点是效率高且没有碎片,缺点是浪费内存;

复制算法常被用于新生代的内存回收。由于在实际场景中,新生代的对象大多会在GC中被回收,因而备用内存可以小一些;实际的做法是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和一块Survivor,GC时将存活对象复制到另一块Survivor空间中;Eden与Survivor的比例为8:1,浪费的空间为10%,尚可接受;当Survivor空间不够用时,可以使用老年代空间进行分配担保;

3. 标记-整理(Mark-Compact)

将所有存活对象移到空间的一端,然后将区域外的空间清理;

4.分代收集

新生代对象“朝生夕死”,使用复制算法;老年代对象存活率高,使用标记-整理或标记清除;

三、保证GC的效率

1.使用OopMap找到GC Root

OopMap是一个记录了对象引用在Java堆栈中的位置的数据结构;

在GC时,GC线程就不需要扫描整个方法区,可以方便的知道每个对象的位置,从而快速完成根节点枚举;

2.安全点(Safe Point)

程序运行期间导致引用变化的指令非常多,每次变化都生成新的OopMap显然是不现实的;事实上,只有当线程运行到安全点(Safepoint)时才停下来开始生成OopMap、进行GC;

安全点的线程同步,即让所有线程同时停在最近的安全点:

  • 抢先式中断:GC发生时,让所有线程暂停,如果暂停的地方不在安全点上,就恢复线程,让它停到最近的安全点上;
  • 主动式中断:设置一个中断标志,线程执行时不断地轮询这个标志,若发现标志为真则把自己中断挂起,标志设置在安全点上;

实际一般用主动式中断;

3.安全区域(Safe Region)

安全点的不足在于,若线程处于Blocked或Waiting状态,那么该线程就无法响应JVM的中断请求,停到安全点上;

安全区域(Safe Region)被用来解决这个问题。安全区域是引用关系不会发生变化的一段代码片段,在这个区域的任意位置开始GC都是安全的。当线程离开Safe Region之前,需要检查系统是否已经完成了GC,若否,则需要等待完成。Safe Region相当于拓宽了安全点,如果说线程在安全区域内Blocked那么GC还是能正常进行的。

参考

  • 《深入理解Java虚拟机(第2版)》 周志明
  • What does Oop Maps means in Hotspot VM exactly