JVM知识点总结(二)——垃圾回收

引用分类:

强引用

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

软引用

是用来描述一些还有用但非必需的对象。在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行二次回收。JDK1.2之后,提供了SoftReference来实现。

弱引用

用来描述非必需对象的,强度比软引用还弱。被弱引用关联的对象只能生存到下一次垃圾回收之前,当垃圾回收器工作时,无论内存是否足够,都会回收掉只被弱引用关联的对象。JDK1.2之后,提供了WeakReference来实现。

虚引用

它是最弱的引用关系,甚至无法用它来取得对象实例。为一个对象设置虚引用关联的唯一目的,就是能在这个对象被垃圾回收器会收时得到一个通知。JDK1.2之后,提供了PhantomReference来实现。

对象是否死亡

通过“GC Roots”作为起点向下搜索,如果搜索完成后对象没有与“GC Roots”相连的引用链,那么标记该对象并筛选出需要执行finalize()方法的对象。如果对象被判定为有必要执行finalize()方法,会被放置到一个F-Queue中。然后GC将对F-Queue中的对象进行第二次标记,标记的依据是该对象在finalize()方法中是否成功拯救自己(只要重新与引用链上的任何一个对象关联即可),第二次标记时它将被移除待回收队列。如果这个时候对象还没有逃脱,那么它就真的可以被回收了。
如图:
JVM知识点总结(二)——垃圾回收_第1张图片

GC Roots的定义:

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

垃圾回收算法

标记-清除算法

是最基础的算法,分为标记和清除两个阶段:首先标记出所有需要回收的对象,标记过程参见上面“对象是否死亡”。这个算法存在两个缺陷:1.标记和清除的效率都很低;2.标记-清除后会产生大量不连续碎片。这种方式可以解决“引用计数”方式无法解决的循环引用问题(参考:JVM 垃圾回收机制)。

复制算法

该算法将内存分为大小相等的两块,每次只用其中一块。若当前内存用完了,就将存活着的对象复制到另一块上面,然后将已使用的过内存一次清理掉。现在的商业虚拟机都采用这种收集算法来回收新生代。由于98%的对象都是“朝生夕死”,所以不需要按照1:1进行内存划分,而是将内存划分为一块较大的Eden空间和两块较小的Survivor空间(Survivor0和Survivor1或者也叫FromSurvivor和ToSurvivor),Hotspot默认Eden:Survivor大小比例为8:1,新生代与老年代的默认比例为2:1。
MinorGC如图:
JVM知识点总结(二)——垃圾回收_第2张图片
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

FromSurvivor和ToSurvivor的空间大小相同,且FromSurvivor的对象会复制到ToSurvivor和老年代中,所以不存在ToSurvivor空间不足以接纳FromSurvivor的情况。我多虑了。

垃圾回收实现

《Java性能权威指南》4种垃圾收集器的整理:
JVM知识点总结(二)——垃圾回收_第3张图片
Hotspot虚拟机的垃圾收集器(之间有连线代表可以搭配使用):
JVM知识点总结(二)——垃圾回收_第4张图片
没有最好的垃圾收集器,只有最合适的垃圾收集器!

参考资料:

  • JVM 新生代老年代
  • 《深入理解Java虚拟机》
  • 《Java性能权威指南》

你可能感兴趣的:(Java精华笔记)