jvm内存回收与性能调优是系统性实践,需理解堆结构(新生代/老年代)、对象生命周期、可达性分析(gc roots)、四类引用及回收器特性,再依场景选g1/zgc/parallel等并调参,同时关注元空间与栈内存。

JVM 内存回收与性能调优不是“配几个参数就完事”的操作,而是围绕对象生命周期、内存布局和回收行为建立的一套系统性实践。核心在于:理解堆怎么分、对象怎么活、垃圾怎么收,再根据应用特征选对回收器、调准关键参数。
堆内存结构决定回收逻辑
堆按对象存活时间划分为新生代(Young)和老年代(Old),这是所有现代GC策略的起点:
- 新生代占堆默认约1/3,内部按8:1:1划分为Eden、Survivor0(From)、Survivor1(To);新对象优先分配在Eden,Minor GC时存活对象复制到To区,并年龄+1
- 对象晋升老年代有三种路径:年龄达阈值(默认15,可调-XX:MaxTenuringThreshold)、Survivor区同龄对象总和超其一半(动态年龄判定)、单个对象超过-XX:PretenureSizeThreshold直接进老年代
- 老年代存放长期存活对象,回收频率低但代价高;CMS已废弃,G1是JDK9+默认,ZGC/Shenandoah适用于毫秒级停顿要求场景
判断对象是否可回收的关键是可达性分析
JVM不靠引用计数(易陷循环引用),而是以GC Roots为起点扫描引用链:
- GC Roots包括:虚拟机栈中局部变量、方法区静态变量、常量池中的常量、本地方法栈JNI引用、同步锁持有的对象等
- 不可达对象会被标记为待回收;但若重写了finalize()且未被调用过,该对象会进入F-Queue队列,有机会“复活”(仅一次,不推荐依赖)
- 四种引用类型影响回收时机:强引用永不回收;软引用在OOM前回收(适合缓存);弱引用下次GC必回收(如WeakHashMap);虚引用仅用于回收通知
调优必须从目标出发,而非盲目套参
没有万能参数,只有匹配业务场景的配置:
- 低延迟场景(如交易、实时推送):选ZGC或Shenandoah,加-XX:MaxGCPauseMillis=10控制目标停顿;避免大对象频繁分配,减少晋升压力
- 高吞吐场景(如批处理、ETL):用Parallel GC(-XX:+UseParallelGC),设-Xms与-Xmx相等防扩容抖动,-XX:ParallelGCThreads匹配CPU核数
- 通用服务(平衡延迟与吞吐):G1最稳妥,调-XX:InitiatingHeapOccupancyPercent=45提前触发并发标记,-XX:G1NewSizePercent=10~30控制新生代弹性范围
- 务必开启GC日志:-Xlog:gc*:file=gc.log:time,uptime,level -XX:+PrintGCDetails,用GCViewer或GCEasy分析停顿分布与晋升速率
元空间和线程栈也需纳入回收视野
堆不是唯一可能OOM的区域:
- 元空间(Metaspace)使用本地内存,默认无上限,但动态生成类过多(如Spring CGLIB代理、热部署框架)会撑爆;应设-XX:MaxMetaspaceSize=256m并监控jstat -gcmetacapacity
- 每个线程独占虚拟机栈,栈深度过大(递归过深)或线程数过多(如未限制线程池)会引发StackOverflowError或直接耗尽内存;可通过-Xss控制单线程栈大小
- 方法区(元空间)回收主要针对不再使用的类:需满足三个条件——该类所有实例已被回收、加载该类的ClassLoader已被回收、该类对应的java.lang.Class对象无任何引用











