Java垃圾收集器
写在前面:
本文是深入理解Java虚拟机第三版(周志明)阅读笔记,图片部分来自电子版书籍
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。
1 确定对象死亡
1.1 引用计数算法(Reference Counting)
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。主流虚拟机都没有使用,优点:简单方便;问题:单纯的引用计数就很难解决对象之间相互循环引用的问题。
1.2 可达性分析算法(Reachability Analysis)
当前主流使用算法,基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

深入理解Java虚拟机第三版 图3-1
Java中可以固定作为GC Roots的对象:
虚拟机栈中引用的对象;
方法区中类静态属性引用对象;
方法区常量引用对象,譬如字符串常量池里的引用;
本地方法栈中JNI(Native方法)引用的对象;
Java虚拟机内部引用;
被同步锁持有的对象;
反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
引用定义的扩展:
JDK1.2之后,引用分为强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。
强引用(Strongly Re-ference):在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用(Soft Reference):是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
弱引用(Weak Reference):也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
虚引用(Phantom英[ˈfæntəm] Reference):也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
注意:可达性分析判定不可达,不一定对象会死亡,宣告对象死亡,至少需要两次标记过程,第一次标记不可达,随后会进行一次筛选,判断是否需要执行finalize()方法,这个方法可能会让对象重新链接(finalize()方法只会执行一次,如果没有覆盖这个方法,虚拟机认为没有必要执行,非常不建议覆盖使用,jdk9已经标记为deprecated)
2 垃圾回收
垃圾收集指可以发生在堆内存和方法区内,一般来说,垃圾回收主要发生在堆中,《Java虚拟机规范》中规定虚拟机可以不实现方法区的垃圾收集。
方法区垃圾回收主要是废弃常量和不再使用的类型。大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义累加载器的场景中,通常需要虚拟机具备类卸载能力。
3 垃圾回收算法
3.1 分代收集理论
一般把Java堆分为新生代(Young Generation)和老年代(Old Generation)。
新生代收集:Minor GC/Young GC;
老年代收集:Major GC/Old GC;
混合收集:Mixed GC 收集整个新生代和部分老年代;
整堆收集:Full GC
3.2 标记-清除算法(Mark-Sweep)
分为标记和清除两个阶段:首先标记需要回收的对象,然后统一回收,是最基础的垃圾收集算法。

深入理解Java虚拟机第三版 图3-2
3.3 标记-复制算法(Mark-Copy)
一般是把新生代分为Eden和两个Survivor空间,HotSpot默认比例是8:1;一般是只使用Eden和一个Survivor。

深入理解Java虚拟机第三版 图3-3
3.4 标记-整理算法(Mark-Compact)

深入理解Java虚拟机第三版 图3-4
4 垃圾收集器(JDK7-JDK11)
下图中存在连线代表可以搭配使用,另外,目前没有一个真正最好的收集器出现,没有万能收集器,需要按需选择。

深入理解Java虚拟机第三版 图3-6
4.1 Serial收集器

深入理解Java虚拟机第三版 图3-7
Serial/Serial Old收集器
4.2 ParNew收集器
ParNew是Serial收集器的多线程并行版本

深入理解Java虚拟机第三版 图3-8
ParNew/Serial Old收集器
4.3 Parallel Scavenge收集器
Parallel Scavenge收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器,Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值
4.4 Serial Old收集器
Serial收集器的老年代版本,使用单线程,标记-整理算法。
4.5 Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发手机,基于标记整理算法。

深入理解Java虚拟机第三版 图3-10
Parallel Scavenge/Parallel Old收集器
4.6 CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS收集器是基于标记-清除算法实现的,整个过程分为四个步骤,包括:
1)初始标记(CMS initial mark):需要STW(stop the world)
2)并发标记(CMS concurrent mark)
3)重新标记(CMS remark):需要STW
4)并发清除(CMS concurrent sweep)

深入理解Java虚拟机第三版 图3-11
CMS收集器
4.7 Garbage First收集器
Garbage First(简称G1)已经是JDK及以上版本的默认垃圾收集器,是目前最新的稳定的垃圾收集器(JDK11之前),主要面向服务端应用。
G1可以面向这个堆内存任何部分来组成回收集(Collection Set,简称CSet)进行回收,衡量标准不在是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,是Mixed GC模式。
G1收集器仍然遵循分代收集理论,也保留有新生代、老年代,但是这个区域不是固定的,都是一些列大小相等的独立区域(Region),每个Region都可以作为Edon,Survivor或者老年代,收集器对不同Region采用不同策略处理。Region是垃圾回收的最小单元。
深入理解Java虚拟机第三版 图3-12
G1收集器Region分区
G1收集器步骤:
1)初始标记
2)并发标记
3)最终标记
4)筛选回收

深入理解Java虚拟机第三版 图3-13
G1收集器
4.8 实验中的垃圾收集器
Shenandoah收集器
ZGG收集器


浙公网安备 33010602011771号