垃圾回收机制
垃圾回收机制(以Java为例)
垃圾回收(GC)
垃圾回收(Garbage Collection),指的是对内存堆中长时间未使用的对象进行回收。
在Java中,垃圾回收通常是由JVM的GC线程自动完成的,开发者不需要手动实现。
如何定义垃圾
引用计数算法
引用计数算法(Reachability
Counting)是通过在对象头中分配一个空间来存储该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加
引用计数算法是将垃圾回收分摊到整个应用程序的运行当中,而不是在进行垃圾收集时挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的Stop-The-World的垃圾收集机制。
但是,现在JVM的垃圾回收机制是Stop-The-World的,考虑这个例子
1 | public class ReferenceCountingGC { |
最后a
和b
两个对象都不再被访问,但是由于他们相互引用,导致它们的引用计数永远不会为0
,也无法通过GC收集器回收它们。
可达性分析算法
可达性分析算法(Reachability Analysis)通过一些被称为引用链(GC
Roots)的对象作为起点,从这些节点开始向下搜索,当一个对象到GC Roots
没有任何引用链相连,即从GC Roots
节点到该节点不可达时,该对象是不可用的。这种算法可以解决引用计数无法解决的循环依赖问题。
在Java语言中,可以作为GC Root
的对象包括
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
1 | public class StackLocalParameter { |
- 方法区中类静态属性引用的对象
1 | public class MethodAreaStaicProperties { |
- 方法区中常量引用的对象
1 | public class MethodAreaStaicProperties { |
- 本地方法栈中JNI即Native方法引用的对象
如何回收垃圾
标记&清除算法(Mark&Sweep)
先把内存区域中的这些对象进行标记,哪些属于可回收标记出来,然后把这些垃圾拎出来清理掉。就像上图一样,清理掉的垃圾就变成未使用的内存区域,等待被再次使用。但这样会遗留很多内存碎片,导致可用堆空间不连续。
复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉,保证了内存的连续可用。但是这种算法代价过高。
标记&整理算法(Mark&Compact)
整理算法不直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。但是,它堆内存变动更加频繁,效率很低。
分代收集算法
分代收集算法是融合上述3种基础的算法思想。对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配,就使用Mark&Sweep或者Mark&Compact算法来进行回收。