一、JVM 中一次完整的 GC 流程(以分代回收为例)
JVM 的垃圾回收(GC)流程依赖于分代收集机制,不同区域(新生代、老年代)采用不同的回收策略。以下是典型的完整 GC 流程(以 Parallel Scavenge + Parallel Old 组合为例):
1. Minor GC(新生代回收)
-
触发条件:
-
新生代(Eden 区)空间不足,无法分配新对象。
-
-
流程:
-
标记存活对象:
从 GC Roots(如线程栈、静态变量等)出发,标记所有存活对象。 -
复制存活对象:
将 Eden 区和当前使用的 Survivor(From 区)中的存活对象复制到另一个 Survivor(To 区),并清空 Eden 和 From 区。 -
年龄增长:
Survivor 区中存活对象的年龄(Age)增加 1。 -
晋升老年代:
若对象年龄达到阈值(默认 15),或 Survivor 区空间不足,存活对象会晋升到老年代。
-
2. Major GC / Full GC(老年代回收)
-
触发条件:
-
老年代空间不足(如晋升失败)。
-
显式调用
System.gc()
(不推荐)。 -
元数据区(Metaspace)空间不足。
-
-
流程(以 Parallel Old 为例):
-
标记存活对象:
标记老年代中所有存活对象。 -
压缩整理(Compaction):
将所有存活对象向老年代一端移动,清理碎片化空间。 -
更新引用:
修正所有指向被移动对象的引用地址。
-
二、对象晋升到老年代的条件
对象从新生代晋升到老年代的条件如下:
-
年龄阈值:
-
对象在 Survivor 区经历的 Minor GC 次数达到阈值(默认 -XX:MaxTenuringThreshold=15)。
-
-
动态年龄判断:
-
如果 Survivor 区中某年龄段的对象总大小超过 Survivor 区的一半,则所有大于等于该年龄的对象直接晋升。
-
-
大对象直接进入老年代:
-
若对象大小超过
-XX:PretenureSizeThreshold
(默认 0,需手动设置),直接分配到老年代。
-
-
Survivor 区空间不足:
-
当 Survivor 区无法容纳 Minor GC 后的存活对象时,直接晋升到老年代。
-
三、重要的 JVM 参数
以下是常见的 JVM 参数分类说明:
1. 堆内存相关
参数 | 作用 | 示例 |
---|---|---|
-Xms | 初始堆大小 | -Xms512m (堆初始 512MB) |
-Xmx | 最大堆大小 | -Xmx4g (堆最大 4GB) |
-Xmn | 新生代大小 | -Xmn1g (新生代 1GB) |
-XX:NewRatio | 新生代与老年代比例 | -XX:NewRatio=2 (老年代:新生代=2:1) |
-XX:SurvivorRatio | Eden 区与 Survivor 区比例 | -XX:SurvivorRatio=8 (Eden:Survivor=8:1:1) |
2. GC 行为控制
参数 | 作用 | 示例 |
---|---|---|
-XX:+UseSerialGC | 使用串行回收器(单线程) | (适合低配机器) |
-XX:+UseParallelGC | 使用 Parallel Scavenge 回收器 | (默认并行回收新生代) |
-XX:+UseConcMarkSweepGC | 使用 CMS 回收器(已废弃) | (低停顿,Java 8 前常用) |
-XX:+UseG1GC | 使用 G1 回收器 | (分区回收,Java 9+ 默认) |
-XX:MaxTenuringThreshold | 对象晋升年龄阈值 | -XX:MaxTenuringThreshold=15 |
-XX:PretenureSizeThreshold | 大对象直接进入老年代的阈值 | -XX:PretenureSizeThreshold=2m |
3. GC 日志与监控
参数 | 作用 | 示例 |
---|---|---|
-XX:+PrintGCDetails | 打印 GC 详细信息 | (需配合日志分析工具) |
-XX:+PrintGCDateStamps | 输出 GC 时间戳 | |
-Xloggc:<path> | 指定 GC 日志文件路径 | -Xloggc:/logs/gc.log |
-XX:+HeapDumpOnOutOfMemoryError | OOM 时生成堆转储 | (用于分析内存泄漏) |
4. 元数据区(Metaspace)
参数 | 作用 | 示例 |
---|---|---|
-XX:MetaspaceSize | 初始元数据区大小 | -XX:MetaspaceSize=128m |
-XX:MaxMetaspaceSize | 元数据区最大大小 | -XX:MaxMetaspaceSize=256m |
四、示例参数配置
# 启动一个 Java 应用,配置堆大小和 G1 回收器
java -Xms2g -Xmx2g \-XX:+UseG1GC \-XX:MaxGCPauseMillis=200 \-XX:MetaspaceSize=128m \-XX:MaxMetaspaceSize=256m \-Xloggc:/logs/gc.log \-jar myapp.jar
五、总结
-
GC 流程:分代回收通过 Minor GC 和 Full GC 协作完成,新生代用复制算法,老年代用标记-清除或标记-整理算法。
-
对象晋升:基于年龄、动态判断、空间不足等因素决定是否晋升到老年代。
-
JVM 参数:需根据应用场景(吞吐量优先或低延迟)选择回收器和调整参数,结合监控工具(如 VisualVM、GCViewer)优化配置。