11.3JVM调优
JVM 内存参数基础
1. 堆内存参数
# 设置堆的初始和最大大小(推荐初始值和最大值一致,避免动态扩容)
java -Xms512m -Xmx512m YourMainClass# 设置新生代大小(新生代 = Eden + 2个Survivor区)
java -Xmn256m YourMainClass# 设置新生代与老年代的比例(默认1:2,-XX:NewRatio=2 表示新生代占1/3)
java -XX:NewRatio=2 YourMainClass# 设置Eden区与Survivor区的比例(默认8:1:1,-XX:SurvivorRatio=8 表示Eden:Survivor=8:1)
java -XX:SurvivorRatio=8 YourMainClass
2. 元空间参数(JDK 8+)
# 设置元空间初始和最大大小(替代JDK 7的永久代 -XX:MaxPermSize)
java -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m YourMainClass
3. 栈内存参数
# 设置每个线程的栈大小(默认1M,减小可增加并发线程数)
java -Xss512k YourMainClass
垃圾回收器选择与优化
JVM 提供多种 GC 策略,需根据应用特点选择:
1. 串行 GC(Serial GC)
# 单线程GC,适合小内存、单核环境
java -XX:+UseSerialGC YourMainClass
- 适用场景:单线程环境,尤其是客户端应用或小型应用程序。
- 它通过暂停所有应用程序线程来进行垃圾回收,因此对于需要高响应性的服务端应用来说不是最佳选择。
- 特点:简单、适合资源受限的环境。
2. 并行 GC(Parallel GC)
Java 8中,默认使用的垃圾回收器是Parallel GC
# 多线程回收,关注吞吐量(CPU利用率)
java -XX:+UseParallelGC YourMainClass# 设置并行GC线程数
java -XX:ParallelGCThreads=4 YourMainClass
- 适用场景:计算密集型、对响应时间要求不高的服务。
- 在Java 8中,默认使用的垃圾回收器是Parallel GC,也称为吞吐量收集器(Throughput Collector)。Parallel GC使用多线程进行年轻代和老年代的垃圾回收,旨在最大化应用程序的吞吐量。这意味着它会尝试在最短的时间内完成垃圾回收过程,但为此可能会导致较长时间的“stop-the-world”暂停,这对于某些对延迟敏感的应用来说可能不是最佳选择。
- 使用多线程进行垃圾回收,旨在最大化应用程序的吞吐量。
- 虽然可以显著减少GC时间,但因为它是“stop-the-world”的,所以在延迟敏感的应用中可能不适用。
- 特点:高效利用多核处理器,提高吞吐量。
3. CMS GC(Concurrent Mark Sweep)
# 低延迟GC,关注响应时间
java -XX:+UseConcMarkSweepGC YourMainClass# 设置CMS线程数
java -XX:ConcGCThreads=2 YourMainClass# 设置老年代使用CMS的触发百分比(默认92%)
java -XX:CMSInitiatingOccupancyFraction=70 YourMainClass
- 适用场景:Web 应用、API 服务(需快速响应)。
- 设计目的是为了最小化停顿时间,适合于需要快速响应的应用程序。
- CMS尝试与应用程序线程并发地执行大部分垃圾收集工作,但在某些阶段仍需要暂停应用程序线程。
- 特点:降低GC导致的停顿时间,但可能会导致更高的CPU使用率。
4. G1 GC(Garbage First)
自Java 9以来成为默认的垃圾回收器
# JDK 9+默认GC,分代不分区,兼顾吞吐量和低延迟
java -XX:+UseG1GC YourMainClass# 设置最大停顿时间目标(毫秒)
java -XX:MaxGCPauseMillis=200 YourMainClass# 设置堆区域大小(默认2048个区域,每区域1-32MB)
java -XX:G1HeapRegionSize=4m YourMainClass
- 适用场景:大内存(>4GB)、多核心服务器。
- 自Java 9以来成为默认的垃圾回收器,在之后的版本中继续得到改进。
- G1是一个服务器风格的垃圾收集器,适用于具有大堆内存或多核处理器的系统。
- 它将堆划分为多个区域,并优先处理包含最多可回收空间的区域,从而提供更可预测的停顿时间。
- 特点:平衡了低延迟和高吞吐量的需求,提供了较好的停顿时间控制。
5. ZGC(JDK 11+)
# 超大型堆(TB级)、极低延迟(<10ms)
java -XX:+UseZGC YourMainClass# 设置ZGC线程数
java -XX:ConcGCThreads=4 YourMainClass
- 适用场景:需要处理 TB 级数据的应用(如大数据、金融交易系统)。
- ZGC是Oracle为解决大型堆上的低延迟需求而开发的新一代垃圾收集器。
- 它的设计目标是能够在TB级别的堆上运行,同时保持非常低的暂停时间(不超过10毫秒),并且这些暂停时间不会随着堆大小的增长而增加。
- 特点:极低的暂停时间,适合对延迟要求严格的大型应用。
6. Shenandoah GC:
- 类似于ZGC,也是设计来解决低延迟问题,由Red Hat贡献给OpenJDK项目。
- 它能够在一个非常大的堆上运作,并且停顿时间不依赖于堆的大小。
- 特点:低延迟,适合长时间运行的服务端应用
-
你需要确保你的JDK版本支持它。如果你使用的是支持Shenandoah的JDK版本(如OpenJDK 12或更新版本),可以通过以下JVM参数启用Shenandoah GC:
-XX:+UseShenandoahGC
例如,启动Java应用程序并指定使用Shenandoah GC,你可以这样设置:
java -XX:+UseShenandoahGC MyApp
请注意,由于Shenandoah GC在某些JDK版本中可能仍然被视为实验性特性,因此其性能和稳定性可能会有所变化。如果你正在使用Oracle JDK,并希望利用Shenandoah GC,但发现默认未包含此功能,考虑切换到提供Shenandoah支持的OpenJDK构建版,如AdoptOpenJDK(现在是Eclipse Temurin),它们可能对Shenandoah有更好的支持。
GC 优化实战策略
1. 新生代大小优化
- 策略:
- 新生代过小 → Minor GC 频繁。
- 新生代过大 → 单次 Minor GC 耗时增加,可能导致 FULL GC。
- 经验值:
- 对于短生命周期对象为主的应用(如 Web 服务),新生代占堆的 1/3~1/2。
- 对于长生命周期对象为主的应用(如缓存服务),适当减小新生代。
-
大对象直接进入老年代:
java -XX:PretenureSizeThreshold=1048576 YourMainClass # 超过1MB的对象直接进入老年代
2. 对象晋升年龄调整
java -XX:MaxTenuringThreshold=15 YourMainClass
# 默认15次Minor GC后晋升到老年代
- 对于短生命周期对象,可降低阈值(如
-XX:MaxTenuringThreshold=3
)。
3.元空间溢出(java.lang.OutOfMemoryError: Metaspace)
- 原因:
- 动态生成大量类(如反射、CGLIB 代理)。
- 元空间大小设置过小。
- 解决:
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m YourMainClass
4. 栈溢出(java.lang.StackOverflowError)
- 原因:
- 方法调用栈过深(如递归无终止条件)。
- 栈空间设置过小。
- 解决:
# 增加栈大小 java -Xss2m YourMainClass# 修复递归逻辑,添加终止条件
JVM 监控与分析工具
1. 命令行工具
- jstat:查看 GC 统计信息
jstat -gc 1234 1000 # 每1秒打印一次进程ID为1234的GC信息
- jmap:生成堆转储文件
jmap -dump:format=b,file=heapdump.hprof 1234 # 生成进程1234的堆转储文件
- jstack:打印线程栈信息(排查死锁)
jstack 1234 > threaddump.txt # 导出进程1234的线程栈
2. 可视化工具
- VisualVM:集成多种监控功能(内存、线程、CPU 等)
jvisualvm # 启动VisualVM
- MAT(Memory Analyzer Tool):分析堆转储文件,定位内存泄漏
- 打开
heapdump.hprof
文件,查看大对象、对象引用链。
- 打开
- G1GC 日志分析:
java -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log YourMainClass
使用工具(如GCViewer)分析 GC 日志。
JVM 内存调优最佳实践
-
优先通过监控工具定位问题:
- 分析 GC 日志 → 确定瓶颈(Minor GC 频繁?FULL GC 耗时过长?)。
- 使用堆转储文件查找内存泄漏。
-
从小规模测试开始:
- 在测试环境验证参数配置,逐步调整至生产环境。
-
避免过度调优:
- 现代 GC(如 G1、ZGC)已针对大多数场景自动优化,仅在必要时手动调整。
-
结合代码优化:
- 减少创建大对象,及时释放资源(如
try-with-resources
)。 - 使用对象池(如 Apache Commons Pool)复用对象。
- 减少创建大对象,及时释放资源(如