JVM的垃圾回收机制
GC的核心目标:自动化内存管理
GC的核心目标就是自动地识别并回收那些不再被程序使用的内存,从而:
1. 解放开发者:让我们不用像C/C++程序员那样手动malloc和free内存,极大地降低了内存泄露和野指针等问题的风险。
2. 保证系统稳定:通过自动化的内存回收,防止因内存耗尽而导致的程序崩溃。
如何判断对象是“垃圾”?
JVM判断一个对象是否可以被回收,主要采用的是可达性分析算法(Reachability Analysis)。
-
基本思想:这个算法将一系列被称为“GC Roots”的特殊对象作为起点,从这些节点开始向下搜索,所有能够被搜索到的对象都被认为是“存活”的,反之,任何从GC Roots不可达的对象,就被判定为“垃圾”。
-
哪些是GC Roots?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象:比如方法内部的局部变量。
- 方法区中类静态属性引用的对象:比如类的静态变量。
- 方法区中常量引用的对象:比如字符串常量池里的引用。
- 本地方法栈中JNI(即Native方法)引用的对象。
- 被同步锁(
synchronized)持有的对象。
核心的垃圾回收算法
找到了垃圾之后,就需要用具体的算法来回收。主要有三种经典算法,它们各有优劣,是后续各种复杂回收器的基础。
-
标记-清除算法(Mark-Sweep)
- 过程:分为“标记”和“清除”两个阶段。首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。
- 优点:实现简单。
- 缺点:会产生大量的内存碎片。不连续的内存空间会导致后续分配大对象时,虽然总空间足够,但找不到一块连续的内存而触发又一次GC。
-
标记-复制算法(Mark-Copy)
- 过程:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 优点:实现简单,不会产生内存碎片,运行高效。
- 缺点:代价是牺牲了一半的内存空间,空间利用率低。
-
标记-整理算法(Mark-Compact)
- 过程:标记过程仍然与“标记-清除”一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉端边界以外的内存。
- 优点:不会产生内存碎片,也不需要牺牲一半的内存空间。
- 缺点:因为涉及到对象的移动,所以效率相对较低。
分代收集理论与内存布局
为了将不同算法的优势结合起来,现代JVM都采用了分代收集(Generational Collection)的理论。这个理论基于两个假说: 1. 绝大多数对象都是“朝生夕死”的。 2. 熬过越多次垃圾回收过程的对象就越难以消亡。
基于此,JVM的堆内存被划分为两个主要区域:
-
新生代(Young Generation):
- 特点:存放生命周期短的对象。绝大多数对象都在这里创建,也在这里消亡。
- 内部结构:分为一个伊甸园区(Eden Space)和两个幸存者区(Survivor Space S0, S1)。
- 回收算法:新生代GC(称为Minor GC)非常频繁,由于存活对象少,所以采用标记-复制算法。新对象在Eden区分配,当Eden区满时触发Minor GC,存活的对象会被复制到其中一个Survivor区(比如S0),同时对象的“年龄”加1。下次GC时,Eden和S0中的存活对象会一起被复制到S1区。如此反复,当一个对象的年龄达到一定阈值(默认15),它就会被“晋升”到老年代。
-
老年代(Old Generation):
- 特点:存放生命周期长的对象,或者一些无法在新生代分配的大对象。
- 回收算法:老年代GC(称为Major GC或Full GC)的频率较低。由于存活对象多,不适合复制算法,所以通常采用标记-清除或标记-整理算法。
主要的垃圾回收器
JVM提供了多种垃圾回收器,它们是上述算法的具体实现,可以根据应用场景进行选择和组合。
- Serial/Serial Old:单线程回收器,简单高效,但会产生较长的“Stop-The-World”(STW,即暂停所有用户线程),适用于客户端模式。
- Parallel Scavenge/Parallel Old:多线程版本的Serial,关注吞吐量(用户代码运行时间 / (用户代码运行时间 + GC时间)),是JDK 8的默认回收器。
- CMS (Concurrent Mark Sweep):第一个以获取最短回收停顿时间为目标的回收器,关注低延迟。它在标记和清除阶段大部分工作可以和用户线程并发执行,但基于标记-清除,会产生内存碎片。
- G1 (Garbage-First):JDK 9及以后的默认回收器,是一个里程碑式的存在。它将堆划分为多个独立的区域(Region),并跟踪每个Region里垃圾的价值,在有限的时间内优先回收价值最大的Region。它在宏观上是标记-整理,局部看是标记-复制,能很好地平衡吞吐量和低延迟。
- ZGC / Shenandoah:最新的超低延迟回收器,追求在任何堆大小下都能将STW时间控制在几毫秒以内,几乎全程并发,是未来发展的方向。
总结来说,JVM的GC机制是一个精密且自适应的系统。它通过可达性分析找到垃圾,然后基于分代理论,在不同区域采用最合适的回收算法(如新生代用复制,老年代用标记-整理),并通过具体的回收器(如G1)来执行,最终目标是在吞TP量和延迟之间找到最佳平衡点,实现高效的自动化内存管理。