JVM调优详解
一、知识概述
JVM调优是Java应用性能优化的重要环节。通过合理配置JVM参数、分析GC行为、优化内存使用,可以显著提升应用性能。理解JVM调优需要在掌握JVM原理的基础上,结合实际应用场景进行实践。
调优目标
┌─────────────────────────────────────────────────────────────────────┐
│ JVM调优目标 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 性能目标 │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ 低延迟 │ │ 高吞吐量 │ │ 小内存占用 │ │
│ │ 响应时间快 │ │ 批处理效率高 │ │ 资源利用率高 │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ │
│ 2. 稳定性目标 │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ 无OOM异常 │ │ GC停顿可控 │ │ 内存无泄漏 │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ │
│ 3. 可预测性目标 │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ 性能可预测 │ │ 资源可控 │ │
│ └───────────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
调优公式:
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + GC时间)
停顿时间 = STW(Stop-The-World)时间总和
内存占用 = 堆内存 + 非堆内存
调优原则
- 不要过早优化 - 先保证正确性,再优化性能
- 基于数据调优 - 使用监控工具收集数据
- 逐步调优 - 每次只调整一个参数
- 对比验证 - 调优前后对比测试
二、知识点详细讲解
2.1 常用JVM参数
2.1.1 内存参数
java
/**
* JVM内存参数详解
*/
public class MemoryParametersDemo {
public static void main(String[] args) {
System.out.println("=== JVM内存参数 ===\n");
printHeapParameters();
printMetaspaceParameters();
printThreadParameters();
printDirectMemoryParameters();
}
private static void printHeapParameters() {
System.out.println("【堆内存参数】\n");
/*
┌─────────────────────────────────────────────────────────────┐
│ 堆内存配置示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ -Xms4g 初始堆大小 4GB │
│ -Xmx4g 最大堆大小 4GB │
│ -Xmn2g 新生代大小 2GB │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Java Heap (4GB) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ ┌───────────────────┐ ┌───────────────────┐ │ │
│ │ │ 新生代 (2GB) │ │ 老年代 (2GB) │ │ │
│ │ │ Eden | S0 | S1 │ │ │ │ │
│ │ └───────────────────┘ └───────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
*/
System.out.println("基本参数:");
System.out.println(" -Xms<size> 初始堆大小");
System.out.println(" -Xmx<size> 最大堆大小");
System.out.println(" -Xmn<size> 新生代大小");
System.out.println();
System.out.println("比例参数:");
System.out.println(" -XX:NewRatio=2 新生代:老年代 = 1:2");
System.out.println(" -XX:SurvivorRatio=8 Eden:Survivor = 8:1:1");
System.out.println(" -XX:MaxTenuringThreshold=15 晋升年龄阈值");
System.out.println(" -XX:PretenureSizeThreshold 大对象阈值");
System.out.println();
System.out.println("推荐配置:");
System.out.println(" -Xms和-Xmx设为相同值(避免动态扩容)");
System.out.println(" 堆大小建议为物理内存的50%-80%");
System.out.println(" 根据对象存活率调整新生代比例");
System.out.println();
System.out.println("示例配置:");
System.out.println(" java -Xms4g -Xmx4g -Xmn2g -XX:SurvivorRatio=8 -jar app.jar");
System.out.println();
}
private static void printMetaspaceParameters() {
System.out.println("【元空间参数】\n");
/*
JDK 8+: 元空间使用本地内存
JDK 7及之前: 永久代使用JVM堆内存
*/
System.out.println("元空间参数:");
System.out.println(" -XX:MetaspaceSize=256m 初始元空间大小");
System.out.println(" -XX:MaxMetaspaceSize=512m 最大元空间大小");
System.out.println(" -XX:MinMetaspaceFreeRatio=40 最小空闲比例");
System.out.println(" -XX:MaxMetaspaceFreeRatio=70 最大空闲比例");
System.out.println();
System.out.println("永久代参数(JDK 7):");
System.out.println(" -XX:PermSize=256m 初始永久代大小");
System.out.println(" -XX:MaxPermSize=512m 最大永久代大小");
System.out.println();
System.out.println("调优建议:");
System.out.println(" - 类加载多的应用需要增大元空间");
System.out.println(" - 动态代理、CGLib生成类多的应用");
System.out.println(" - 设置合理的MaxMetaspaceSize避免无限制增长");
System.out.println();
}
private static void printThreadParameters() {
System.out.println("【线程参数】\n");
System.out.println("线程栈参数:");
System.out.println(" -Xss512k 线程栈大小");
System.out.println();
System.out.println("调优说明:");
System.out.println(" - 默认: Linux 1MB, Windows 320KB");
System.out.println(" - 递归深的程序可增大(如-Xss2m)");
System.out.println(" - 线程多的程序可减小(如-Xss256k)");
System.out.println(" - 总栈内存 = 线程数 × 栈大小");
System.out.println();
}
private static void printDirectMemoryParameters() {
System.out.println("【直接内存参数】\n");
System.out.println("直接内存参数:");
System.out.println(" -XX:MaxDirectMemorySize=1g 最大直接内存");
System.out.println();
System.out.println("适用场景:");
System.out.println(" - NIO应用(Netty、Kafka等)");
System.out.println(" - 大文件IO");
System.out.println(" - 零拷贝场景");
System.out.println();
System.out.println("说明:");
System.out.println(" - 默认等于最大堆大小");
System.out.println(" - 超出限制抛出OutOfMemoryError: Direct buffer memory");
System.out.println();
}
}
2.1.2 GC参数
java
/**
* GC参数详解
*/
public class GCParametersDemo {
public static void main(String[] args) {
System.out.println("=== GC参数 ===\n");
printGCSelector();
printGCConfiguration();
printGCTuning();
}
private static void printGCSelector() {
System.out.println("【GC选择参数】\n");
System.out.println("JDK 8:");
System.out.println(" -XX:+UseSerialGC Serial GC");
System.out.println(" -XX:+UseParallelGC Parallel GC (默认)");
System.out.println(" -XX:+UseParallelOldGC Parallel Old GC");
System.out.println(" -XX:+UseConcMarkSweepGC CMS GC");
System.out.println(" -XX:+UseG1GC G1 GC");
System.out.println();
System.out.println("JDK 11+:");
System.out.println(" -XX:+UseG1GC G1 GC (默认)");
System.out.println(" -XX:+UseZGC ZGC");
System.out.println(" -XX:+UseShenandoahGC Shenandoah GC");
System.out.println();
System.out.println("JDK 21+:");
System.out.println(" -XX:+UseZGC -XX:+ZGenerational 分代ZGC");
System.out.println();
System.out.println("选择建议:");
System.out.println("┌─────────────────┬────────────────────┐");
System.out.println("│ 场景 │ 推荐GC │");
System.out.println("├─────────────────┼────────────────────┤");
System.out.println("│ 单核/小内存 │ Serial GC │");
System.out.println("│ 多核/吞吐优先 │ Parallel GC │");
System.out.println("│ 多核/延迟优先 │ G1 GC │");
System.out.println("│ 大堆/低延迟 │ ZGC │");
System.out.println("└─────────────────┴────────────────────┘");
System.out.println();
}
private static void printGCConfiguration() {
System.out.println("【GC配置参数】\n");
System.out.println("Parallel GC:");
System.out.println(" -XX:MaxGCPauseMillis=200 最大停顿时间目标");
System.out.println(" -XX:GCTimeRatio=99 吞吐量目标");
System.out.println(" -XX:ParallelGCThreads=8 GC线程数");
System.out.println();
System.out.println("CMS GC (JDK 8):");
System.out.println(" -XX:CMSInitiatingOccupancyFraction=75 老年代占用阈值");
System.out.println(" -XX:+UseCMSCompactAtFullCollection Full GC时压缩");
System.out.println(" -XX:CMSFullGCsBeforeCompaction=0 压缩间隔");
System.out.println(" -XX:+CMSParallelRemarkEnabled 并行重新标记");
System.out.println();
System.out.println("G1 GC:");
System.out.println(" -XX:MaxGCPauseMillis=200 最大停顿时间");
System.out.println(" -XX:G1HeapRegionSize=4m Region大小");
System.out.println(" -XX:InitiatingHeapOccupancyPercent=45 堆占用阈值");
System.out.println(" -XX:G1ReservePercent=15 预留空间");
System.out.println(" -XX:G1HeapWastePercent=5 浪费空间阈值");
System.out.println();
System.out.println("ZGC:");
System.out.println(" -XX:ZCollectionInterval=5 GC触发间隔(ms)");
System.out.println(" -XX:ZAllocationSpikeTolerance=2 分配峰值容忍度");
System.out.println(" -XX:ConcGCThreads=2 并发GC线程数");
System.out.println();
}
private static void printGCTuning() {
System.out.println("【GC调优参数】\n");
System.out.println("对象晋升:");
System.out.println(" -XX:MaxTenuringThreshold=15 晋升年龄阈值");
System.out.println(" -XX:TargetSurvivorRatio=50 Survivor目标使用率");
System.out.println();
System.out.println("大对象处理:");
System.out.println(" -XX:PretenureSizeThreshold=1m 大对象阈值(字节)");
System.out.println(" -XX:+UseTLAB 使用TLAB(默认开启)");
System.out.println(" -XX:TLABSize=256k TLAB大小");
System.out.println();
System.out.println("GC日志(JDK 8):");
System.out.println(" -XX:+PrintGCDetails 详细GC信息");
System.out.println(" -XX:+PrintGCDateStamps 日期时间戳");
System.out.println(" -XX:+PrintGCTimeStamps 相对时间戳");
System.out.println(" -XX:+PrintGCApplicationStoppedTime 停顿时间");
System.out.println(" -Xloggc:gc.log 日志文件");
System.out.println(" -XX:+UseGCLogFileRotation 日志轮转");
System.out.println(" -XX:NumberOfGCLogFiles=10 日志文件数");
System.out.println(" -XX:GCLogFileSize=10m 单文件大小");
System.out.println();
System.out.println("GC日志(JDK 11+):");
System.out.println(" -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=10,filesize=10m");
System.out.println();
}
}
2.2 监控工具
2.2.1 命令行工具
java
/**
* JVM监控命令行工具
*/
public class CommandLineToolsDemo {
public static void main(String[] args) {
System.out.println("=== JVM监控命令行工具 ===\n");
jpsDemo();
jstatDemo();
jmapDemo();
jstackDemo();
jinfoDemo();
}
private static void jpsDemo() {
System.out.println("【jps - Java进程状态工具】\n");
/*
jps [options] [hostid]
作用: 列出正在运行的Java进程
*/
System.out.println("常用命令:");
System.out.println(" jps 列出Java进程");
System.out.println(" jps -l 显示主类全名");
System.out.println(" jps -m 显示JVM参数");
System.out.println(" jps -v 显示传递给main的参数");
System.out.println();
System.out.println("示例输出:");
System.out.println(" 12345 MyApp");
System.out.println(" 23456 org.apache.catalina.startup.Bootstrap");
System.out.println(" 34567 sun.tools.jps.Jps");
System.out.println();
}
private static void jstatDemo() {
System.out.println("【jstat - 统计监控工具】\n");
/*
jstat [option vmid [interval[s|ms] [count]]]
作用: 监控类加载、内存、GC等信息
*/
System.out.println("常用选项:");
System.out.println(" -class 类加载统计");
System.out.println(" -gc 堆内存统计");
System.out.println(" -gcutil GC统计(百分比)");
System.out.println(" -gccause GC原因");
System.out.println(" -gccapacity 堆内存容量");
System.out.println(" -compiler JIT编译统计");
System.out.println(" -printcompilation JIT编译方法");
System.out.println();
System.out.println("常用命令:");
System.out.println(" jstat -gc 12345 1000 10 每秒打印GC信息,共10次");
System.out.println(" jstat -gcutil 12345 打印GC使用率");
System.out.println();
System.out.println("输出示例 (jstat -gcutil 12345):");
System.out.println(" S0 S1 E O M CCS YGC YGCT FGC FGCT");
System.out.println(" 0.00 50.00 45.23 67.89 80.12 70.34 15 0.234 2 0.456");
System.out.println();
System.out.println("字段说明:");
System.out.println(" S0, S1: Survivor使用率");
System.out.println(" E: Eden使用率");
System.out.println(" O: 老年代使用率");
System.out.println(" M: 元空间使用率");
System.out.println(" YGC: Young GC次数");
System.out.println(" YGCT: Young GC时间");
System.out.println(" FGC: Full GC次数");
System.out.println(" FGCT: Full GC时间");
System.out.println();
}
private static void jmapDemo() {
System.out.println("【jmap - 内存映射工具】\n");
/*
jmap [option] vmid
作用: 生成堆转储、查看内存信息
*/
System.out.println("常用选项:");
System.out.println(" -heap 打印堆摘要");
System.out.println(" -histo 打印对象统计");
System.out.println(" -histo:live 只统计存活对象(触发GC)");
System.out.println(" -dump:format=b,file=heap.hprof 生成堆转储");
System.out.println();
System.out.println("常用命令:");
System.out.println(" jmap -heap 12345 查看堆配置");
System.out.println(" jmap -histo 12345 对象统计");
System.out.println(" jmap -histo:live 12345 存活对象统计");
System.out.println(" jmap -dump:format=b,file=heap.hprof 12345 堆转储");
System.out.println();
System.out.println("堆转储分析:");
System.out.println(" 1. 使用jvisualvm打开.hprof文件");
System.out.println(" 2. 使用MAT(Memory Analyzer Tool)分析");
System.out.println(" 3. 在线工具: https://heaphero.io");
System.out.println();
}
private static void jstackDemo() {
System.out.println("【jstack - 线程堆栈工具】\n");
/*
jstack [option] vmid
作用: 打印线程堆栈,诊断死锁、性能问题
*/
System.out.println("常用选项:");
System.out.println(" -l 打印锁信息");
System.out.println(" -m 打印本地方法帧");
System.out.println(" -F 强制打印(无响应时)");
System.out.println();
System.out.println("常用命令:");
System.out.println(" jstack 12345 打印线程堆栈");
System.out.println(" jstack -l 12345 打印锁信息");
System.out.println(" jstack 12345 > thread.txt 保存到文件");
System.out.println();
System.out.println("应用场景:");
System.out.println(" 1. CPU使用率高 - 找到占用CPU的线程");
System.out.println(" 2. 死锁检测 - 查看锁等待关系");
System.out.println(" 3. 线程阻塞 - 分析线程等待原因");
System.out.println();
System.out.println("CPU高使用率排查:");
System.out.println(" 1. top -H -p <pid> 找到占用CPU的线程");
System.out.println(" 2. printf '%x' <tid> 线程ID转16进制");
System.out.println(" 3. jstack <pid> | grep <hex-tid> 定位线程堆栈");
System.out.println();
}
private static void jinfoDemo() {
System.out.println("【jinfo - 配置信息工具】\n");
/*
jinfo [option] vmid
作用: 查看和修改JVM参数
*/
System.out.println("常用选项:");
System.out.println(" -flags 打印所有JVM参数");
System.out.println(" -flag <name> 打印指定参数");
System.out.println(" -flag [+|-]<name> 开启/关闭参数");
System.out.println(" -flag <name>=<val> 设置参数值");
System.out.println();
System.out.println("常用命令:");
System.out.println(" jinfo -flags 12345 查看所有参数");
System.out.println(" jinfo -flag MaxHeapSize 12345 查看堆大小");
System.out.println(" jinfo -flag +PrintGC 12345 开启GC打印");
System.out.println(" jinfo -flag MaxHeapFreeRatio=70 12345 设置参数");
System.out.println();
System.out.println("说明:");
System.out.println(" - 部分参数可在运行时修改(Manageable)");
System.out.println(" - jinfo -flags可查看哪些可修改");
System.out.println();
}
}
2.2.2 可视化工具
java
/**
* JVM可视化监控工具
*/
public class VisualToolsDemo {
public static void main(String[] args) {
System.out.println("=== JVM可视化监控工具 ===\n");
jconsoleDemo();
jvisualvmDemo();
jmcDemo();
thirdPartyTools();
}
private static void jconsoleDemo() {
System.out.println("【JConsole】\n");
/*
JDK自带的监控工具
启动命令: jconsole
*/
System.out.println("功能:");
System.out.println(" - 内存监控(堆、非堆、各内存池)");
System.out.println(" - 线程监控(线程数、死锁检测)");
System.out.println(" - 类加载监控");
System.out.println(" - MBean浏览和操作");
System.out.println(" - CPU使用率监控");
System.out.println();
System.out.println("使用场景:");
System.out.println(" - 快速查看JVM状态");
System.out.println(" - 内存使用趋势");
System.out.println(" - 线程状态概览");
System.out.println();
System.out.println("启动:");
System.out.println(" jconsole");
System.out.println(" jconsole <pid>");
System.out.println();
}
private static void jvisualvmDemo() {
System.out.println("【JVisualVM】\n");
/*
JDK 8及之前自带
JDK 11+需要单独下载: https://visualvm.github.io
*/
System.out.println("功能:");
System.out.println(" - 内存分析(堆转储、对象统计)");
System.out.println(" - CPU分析(采样、跟踪)");
System.out.println(" - 线程分析(线程转储、时间线)");
System.out.println(" - GC可视化");
System.out.println(" - 插件扩展(VisualGC、BTrace等)");
System.out.println();
System.out.println("常用操作:");
System.out.println(" 1. 监控 - 查看CPU、内存、线程实时数据");
System.out.println(" 2. 堆转储 - 捕获当前堆状态");
System.out.println(" 3. 分析堆转储 - 查找内存泄漏");
System.out.println(" 4. CPU采样 - 分析热点方法");
System.out.println();
System.out.println("内存泄漏分析步骤:");
System.out.println(" 1. 执行Heap Dump");
System.out.println(" 2. 查看Classes,找到实例数多的类");
System.out.println(" 3. 双击类查看实例");
System.out.println(" 4. 右键实例 -> Nearest GC Root");
System.out.println(" 5. 分析引用链,找到泄漏源");
System.out.println();
System.out.println("启动:");
System.out.println(" jvisualvm (JDK 8)");
System.out.println(" visualvm (JDK 11+,需单独安装)");
System.out.println();
}
private static void jmcDemo() {
System.out.println("【JMC - Java Mission Control】\n");
/*
Oracle官方工具,JDK 11+需要单独下载
*/
System.out.println("功能:");
System.out.println(" - JMX控制台监控");
System.out.println(" - Java Flight Recorder (JFR) 分析");
System.out.println(" - 低开销生产环境监控");
System.out.println(" - 详细的事件分析");
System.out.println();
System.out.println("JFR (Java Flight Recorder):");
System.out.println(" - 内置于JVM的低开销事件收集");
System.out.println(" - 开销<1%,适合生产环境");
System.out.println(" - 记录GC、编译、类加载、线程等事件");
System.out.println();
System.out.println("启用JFR:");
System.out.println(" java -XX:+UnlockCommercialFeatures \\");
System.out.println(" -XX:+FlightRecorder \\");
System.out.println(" -XX:StartFlightRecording=duration=60s,filename=recording.jfr \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("下载:");
System.out.println(" https://www.oracle.com/java/technologies/jdk-mission-control.html");
System.out.println();
}
private static void thirdPartyTools() {
System.out.println("【第三方工具】\n");
System.out.println("1. MAT (Memory Analyzer Tool)");
System.out.println(" - Eclipse项目,专业的堆转储分析");
System.out.println(" - 内存泄漏检测、对象分析");
System.out.println(" - 下载: https://eclipse.dev/mat/");
System.out.println();
System.out.println("2. GCEasy");
System.out.println(" - 在线GC日志分析");
System.out.println(" - 网址: https://gceasy.io");
System.out.println(" - 自动分析GC问题并给出建议");
System.out.println();
System.out.println("3. GCViewer");
System.out.println(" - 开源GC日志可视化");
System.out.println(" - 下载: https://github.com/chewiebug/GCViewer");
System.out.println();
System.out.println("4. Arthas");
System.out.println(" - 阿里开源的Java诊断工具");
System.out.println(" - 在线诊断、无需重启");
System.out.println(" - 功能: 监控、反编译、追踪等");
System.out.println(" - 下载: https://arthas.aliyun.com");
System.out.println();
System.out.println("5. Perfino");
System.out.println(" - 商业JVM监控工具");
System.out.println(" - 企业级APM解决方案");
System.out.println();
System.out.println("6. YourKit");
System.out.println(" - 商业Java性能分析器");
System.out.println(" - CPU、内存、线程分析");
System.out.println();
}
}
2.3 性能问题排查
2.3.1 内存问题排查
java
import java.util.*;
import java.lang.management.*;
/**
* 内存问题排查示例
*/
public class MemoryIssueDemo {
public static void main(String[] args) {
System.out.println("=== 内存问题排查 ===\n");
oomDiagnosis();
memoryLeakDiagnosis();
metaspaceOOMDiagnosis();
}
/**
* OOM诊断
*/
private static void oomDiagnosis() {
System.out.println("【OOM诊断】\n");
System.out.println("OOM类型及原因:");
System.out.println();
System.out.println("1. Java heap space");
System.out.println(" 原因: 堆内存不足或内存泄漏");
System.out.println(" 排查: jmap -histo, MAT分析堆转储");
System.out.println(" 解决: 增大-Xmx, 修复内存泄漏");
System.out.println();
System.out.println("2. Metaspace");
System.out.println(" 原因: 类加载过多或元空间设置过小");
System.out.println(" 排查: jstat -class, 检查类加载");
System.out.println(" 解决: 增大-XX:MaxMetaspaceSize");
System.out.println();
System.out.println("3. GC overhead limit exceeded");
System.out.println(" 原因: GC耗时过长但回收效果差");
System.out.println(" 排查: 分析GC日志");
System.out.println(" 解决: 增大堆内存, 优化对象创建");
System.out.println();
System.out.println("4. Direct buffer memory");
System.out.println(" 原因: NIO直接内存不足");
System.out.println(" 排查: 检查DirectByteBuffer使用");
System.out.println(" 解决: 增大-XX:MaxDirectMemorySize");
System.out.println();
System.out.println("5. unable to create new native thread");
System.out.println(" 原因: 线程数过多,系统资源不足");
System.out.println(" 排查: jstack, 检查线程数");
System.out.println(" 解决: 减少线程数, 增大系统限制");
System.out.println();
System.out.println("OOM排查步骤:");
System.out.println(" 1. 开启自动堆转储:");
System.out.println(" -XX:+HeapDumpOnOutOfMemoryError");
System.out.println(" -XX:HeapDumpPath=/path/to/dump");
System.out.println(" 2. 发生OOM后获取堆转储文件");
System.out.println(" 3. 使用MAT或JVisualVM分析");
System.out.println(" 4. 查找大对象和GC Root路径");
System.out.println(" 5. 定位泄漏代码并修复");
System.out.println();
}
/**
* 内存泄漏诊断
*/
private static void memoryLeakDiagnosis() {
System.out.println("【内存泄漏诊断】\n");
System.out.println("内存泄漏常见场景:");
System.out.println();
System.out.println("1. 静态集合类引用");
System.out.println(" 示例: static Map<String, Object> cache");
System.out.println(" 问题: 只添加不删除");
System.out.println(" 解决: 使用弱引用(WeakHashMap)或定期清理");
System.out.println();
System.out.println("2. 监听器未注销");
System.out.println(" 示例: 注册事件监听器但未移除");
System.out.println(" 解决: 使用WeakReference或显式移除");
System.out.println();
System.out.println("3. ThreadLocal未清理");
System.out.println(" 示例: ThreadLocal在线程池场景");
System.out.println(" 解决: finally中调用remove()");
System.out.println();
System.out.println("4. 数据库/IO连接未关闭");
System.out.println(" 示例: JDBC Connection未close");
System.out.println(" 解决: try-with-resources");
System.out.println();
System.out.println("5. 缓存无限制增长");
System.out.println(" 示例: 无过期策略的缓存");
System.out.println(" 解决: 使用Caffeine/Guava Cache");
System.out.println();
System.out.println("诊断方法:");
System.out.println(" 1. 多次触发GC,观察内存是否持续增长");
System.out.println(" 2. 对比两次堆转储,找出增长的对象");
System.out.println(" 3. 分析GC Root,找到引用链");
System.out.println(" 4. 定位代码位置修复");
System.out.println();
}
/**
* 元空间OOM诊断
*/
private static void metaspaceOOMDiagnosis() {
System.out.println("【元空间OOM诊断】\n");
System.out.println("原因分析:");
System.out.println(" 1. 加载类过多");
System.out.println(" 2. 动态代理/CGLib生成类");
System.out.println(" 3. JSP编译成类过多");
System.out.println(" 4. 反射使用过多");
System.out.println();
System.out.println("排查命令:");
System.out.println(" jstat -class <pid> 查看类加载统计");
System.out.println(" jmap -clstats <pid> 查看类加载器统计");
System.out.println(" jcmd <pid> VM.classloader_stats 类加载器详情");
System.out.println();
System.out.println("解决方案:");
System.out.println(" 1. 增大元空间: -XX:MaxMetaspaceSize=512m");
System.out.println(" 2. 限制动态代理生成: sun.misc.ProxyGenerator.saveGeneratedFiles");
System.out.println(" 3. 优化JSP,减少类生成");
System.out.println(" 4. 检查类加载器泄漏");
System.out.println();
}
}
2.3.2 CPU问题排查
java
import java.util.*;
/**
* CPU问题排查示例
*/
public class CPUIssueDemo {
public static void main(String[] args) {
System.out.println("=== CPU问题排查 ===\n");
highCPUDiagnosis();
threadBlockDiagnosis();
deadlockDiagnosis();
}
/**
* CPU使用率高排查
*/
private static void highCPUDiagnosis() {
System.out.println("【CPU使用率高排查】\n");
System.out.println("排查步骤:");
System.out.println();
System.out.println("Step 1: 找到高CPU进程");
System.out.println(" top");
System.out.println(" ps aux | grep java");
System.out.println();
System.out.println("Step 2: 找到高CPU线程");
System.out.println(" top -H -p <pid>");
System.out.println(" # 查看进程下的线程CPU使用率");
System.out.println();
System.out.println("Step 3: 线程ID转16进制");
System.out.println(" printf '%x' <tid>");
System.out.println(" # 例如: printf '%x' 12345 -> 3039");
System.out.println();
System.out.println("Step 4: 定位线程堆栈");
System.out.println(" jstack <pid> | grep <hex-tid>");
System.out.println(" jstack <pid> | grep -A 20 3039");
System.out.println();
System.out.println("完整示例:");
System.out.println(" # 1. 找到Java进程PID");
System.out.println(" jps");
System.out.println(" # 假设PID是 12345");
System.out.println();
System.out.println(" # 2. 找到高CPU线程");
System.out.println(" top -H -p 12345");
System.out.println(" # 假设线程TID是 12350");
System.out.println();
System.out.println(" # 3. 转换TID为16进制");
System.out.println(" printf '%x' 12350");
System.out.println(" # 输出: 303e");
System.out.println();
System.out.println(" # 4. 查看线程堆栈");
System.out.println(" jstack 12345 | grep -A 20 303e");
System.out.println();
System.out.println("常见原因:");
System.out.println(" 1. 死循环");
System.out.println(" 2. 频繁GC");
System.out.println(" 3. 正则表达式回溯");
System.out.println(" 4. 加密算法计算量大");
System.out.println(" 5. 序列化/反序列化");
System.out.println();
}
/**
* 线程阻塞排查
*/
private static void threadBlockDiagnosis() {
System.out.println("【线程阻塞排查】\n");
System.out.println("查看线程状态:");
System.out.println(" jstack <pid>");
System.out.println();
System.out.println("线程状态说明:");
System.out.println(" NEW 新建");
System.out.println(" RUNNABLE 运行中");
System.out.println(" BLOCKED 阻塞(等待锁)");
System.out.println(" WAITING 等待(无限期)");
System.out.println(" TIMED_WAITING 超时等待");
System.out.println(" TERMINATED 终止");
System.out.println();
System.out.println("常见阻塞原因:");
System.out.println(" 1. 等待锁 - synchronized");
System.out.println(" 2. 等待IO - 网络、文件");
System.out.println(" 3. 等待通知 - wait/notify");
System.out.println(" 4. 等待条件 - Condition.await");
System.out.println(" 5. 等待任务 - Future.get");
System.out.println(" 6. 睡眠 - Thread.sleep");
System.out.println();
System.out.println("jstack输出示例:");
System.out.println("""
"thread-1" prio=5 tid=0x... nid=0x... waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.MyClass.method(MyClass.java:100)
- waiting to lock <0x...> (a java.lang.Object)
- locked <0x...> (a java.lang.Object)
...
""");
System.out.println();
}
/**
* 死锁排查
*/
private static void deadlockDiagnosis() {
System.out.println("【死锁排查】\n");
System.out.println("死锁检测:");
System.out.println(" jstack -l <pid>");
System.out.println(" # 会自动检测死锁并打印");
System.out.println();
System.out.println("死锁输出示例:");
System.out.println("""
Found one Java-level deadlock:
=============================
"thread-1":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object),
which is held by "thread-2"
"thread-2":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object),
which is held by "thread-1"
Java stack information for the threads listed above:
===================================================
"thread-1":
at com.example.DeadlockDemo.run(DeadlockDemo.java:20)
- waiting to lock <0x...> (a java.lang.Object)
- locked <0x...> (a java.lang.Object)
"thread-2":
at com.example.DeadlockDemo.run(DeadlockDemo.java:30)
- waiting to lock <0x...> (a java.lang.Object)
- locked <0x...> (a java.lang.Object)
Found 1 deadlock.
""");
System.out.println();
System.out.println("死锁预防:");
System.out.println(" 1. 固定加锁顺序");
System.out.println(" 2. 使用tryLock超时");
System.out.println(" 3. 使用Lock代替synchronized");
System.out.println(" 4. 减小锁粒度");
System.out.println(" 5. 使用无锁数据结构");
System.out.println();
}
}
2.4 调优实践案例
java
/**
* 调优实践案例
*/
public class TuningCaseDemo {
public static void main(String[] args) {
System.out.println("=== 调优实践案例 ===\n");
case1_HighThroughput();
case2_LowLatency();
case3_BigHeap();
case4_Microservice();
}
/**
* 案例1: 批处理应用 - 高吞吐量
*/
private static void case1_HighThroughput() {
System.out.println("【案例1: 批处理应用 - 高吞吐量】\n");
System.out.println("场景特点:");
System.out.println(" - 离线数据处理");
System.out.println(" - 关注吞吐量");
System.out.println(" - 可接受较长GC停顿");
System.out.println();
System.out.println("调优目标:");
System.out.println(" - 最大化吞吐量");
System.out.println(" - 减少GC总时间占比");
System.out.println();
System.out.println("推荐配置:");
System.out.println(" java -Xms8g -Xmx8g \\");
System.out.println(" -XX:+UseParallelGC \\");
System.out.println(" -XX:NewRatio=1 \\");
System.out.println(" -XX:SurvivorRatio=6 \\");
System.out.println(" -XX:MaxTenuringThreshold=10 \\");
System.out.println(" -XX:ParallelGCThreads=8 \\");
System.out.println(" -XX:GCTimeRatio=19 \\");
System.out.println(" -XX:MaxGCPauseMillis=500 \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("配置说明:");
System.out.println(" - UseParallelGC: 吞吐量优先收集器");
System.out.println(" - NewRatio=1: 新生代占一半(对象存活率高)");
System.out.println(" - GCTimeRatio=19: 目标吞吐量95%");
System.out.println(" - MaxGCPauseMillis=500: 允许较长停顿");
System.out.println();
}
/**
* 案例2: Web应用 - 低延迟
*/
private static void case2_LowLatency() {
System.out.println("【案例2: Web应用 - 低延迟】\n");
System.out.println("场景特点:");
System.out.println(" - 在线服务");
System.out.println(" - 关注响应时间");
System.out.println(" - 要求低GC停顿");
System.out.println();
System.out.println("调优目标:");
System.out.println(" - GC停顿 < 100ms");
System.out.println(" - 避免Full GC");
System.out.println();
System.out.println("推荐配置 (G1 GC):");
System.out.println(" java -Xms4g -Xmx4g \\");
System.out.println(" -XX:+UseG1GC \\");
System.out.println(" -XX:MaxGCPauseMillis=100 \\");
System.out.println(" -XX:G1HeapRegionSize=8m \\");
System.out.println(" -XX:InitiatingHeapOccupancyPercent=35 \\");
System.out.println(" -XX:G1ReservePercent=20 \\");
System.out.println(" -XX:+ExplicitGCInvokesConcurrent \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("推荐配置 (ZGC):");
System.out.println(" java -Xms4g -Xmx4g \\");
System.out.println(" -XX:+UseZGC \\");
System.out.println(" -XX:ConcGCThreads=2 \\");
System.out.println(" -XX:ZCollectionInterval=5 \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("配置说明:");
System.out.println(" - UseG1GC: 延迟优先收集器");
System.out.println(" - MaxGCPauseMillis=100: 控制停顿时间");
System.out.println(" - InitiatingHeapOccupancyPercent=35: 提前触发Mixed GC");
System.out.println();
}
/**
* 案例3: 大内存应用
*/
private static void case3_BigHeap() {
System.out.println("【案例3: 大内存应用】\n");
System.out.println("场景特点:");
System.out.println(" - 堆内存 > 16GB");
System.out.println(" - 缓存型应用");
System.out.println(" - 长时间运行");
System.out.println();
System.out.println("推荐配置:");
System.out.println(" java -Xms32g -Xmx32g \\");
System.out.println(" -XX:+UseZGC \\");
System.out.println(" -XX:+ZGenerational \\");
System.out.println(" -XX:ConcGCThreads=4 \\");
System.out.println(" -XX:ZCollectionInterval=10 \\");
System.out.println(" -XX:ZAllocationSpikeTolerance=4 \\");
System.out.println(" -XX:+UnlockExperimentalVMOptions \\");
System.out.println(" -XX:+UseTransparentHugePages \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("配置说明:");
System.out.println(" - UseZGC: 支持大堆、低延迟");
System.out.println(" - ZGenerational: 分代ZGC (JDK 21+)");
System.out.println(" - UseTransparentHugePages: 使用大页内存");
System.out.println();
}
/**
* 案例4: 微服务应用
*/
private static void case4_Microservice() {
System.out.println("【案例4: 微服务应用】\n");
System.out.println("场景特点:");
System.out.println(" - 容器化部署");
System.out.println(" - 内存有限(2-4GB)");
System.out.println(" - 多实例运行");
System.out.println();
System.out.println("推荐配置:");
System.out.println(" java -Xms1536m -Xmx1536m \\");
System.out.println(" -XX:+UseG1GC \\");
System.out.println(" -XX:MaxGCPauseMillis=50 \\");
System.out.println(" -XX:G1HeapRegionSize=4m \\");
System.out.println(" -XX:InitiatingHeapOccupancyPercent=30 \\");
System.out.println(" -XX:MaxMetaspaceSize=256m \\");
System.out.println(" -XX:CompressedClassSpaceSize=128m \\");
System.out.println(" -XX:+UseStringDeduplication \\");
System.out.println(" -Xss512k \\");
System.out.println(" -XX:+UseContainerSupport \\");
System.out.println(" -XX:MaxRAMPercentage=75.0 \\");
System.out.println(" -jar app.jar");
System.out.println();
System.out.println("配置说明:");
System.out.println(" - UseContainerSupport: 自动感知容器内存限制");
System.out.println(" - MaxRAMPercentage: 使用容器内存的百分比");
System.out.println(" - UseStringDeduplication: 字符串去重减少内存");
System.out.println(" - Xss512k: 减小线程栈节省内存");
System.out.println();
}
}
三、总结与最佳实践
调优流程
┌─────────────────────────────────────────────────────────────────────┐
│ JVM调优标准流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 明确目标 │
│ ↓ │
│ 2. 收集数据 (监控工具、GC日志) │
│ ↓ │
│ 3. 分析问题 (内存、CPU、GC) │
│ ↓ │
│ 4. 制定方案 (调整参数、优化代码) │
│ ↓ │
│ 5. 实施调优 (每次只改一个参数) │
│ ↓ │
│ 6. 验证效果 (压测、对比) │
│ ↓ │
│ 7. 持续监控 (生产环境观察) │
│ ↓ │
│ 8. 循环迭代 │
│ │
└─────────────────────────────────────────────────────────────────────┘
调优检查清单
markdown
## JVM调优检查清单
### 启动参数
- [ ] -Xms和-Xmx设置为相同值
- [ ] 选择合适的GC收集器
- [ ] 开启GC日志
- [ ] 设置合理的元空间大小
### 内存配置
- [ ] 堆内存 ≤ 物理内存的80%
- [ ] 新生代比例合适(根据对象存活率)
- [ ] 元空间有上限
- [ ] 直接内存有上限(如使用NIO)
### GC配置
- [ ] GC停顿时间目标合理
- [ ] 吞吐量目标合理
- [ ] 大对象阈值合适
- [ ] 晋升年龄阈值合适
### 监控
- [ ] GC日志保留
- [ ] 堆转储机制(OOM时自动dump)
- [ ] 监控告警(内存、CPU、GC)
- [ ] 性能基线数据
### 代码层面
- [ ] 避免大对象频繁创建
- [ ] 避免内存泄漏
- [ ] 合理使用缓存
- [ ] 线程池配置合理
常用调优命令速查
bash
# 查看Java进程
jps -l
# 查看GC统计
jstat -gcutil <pid> 1000
# 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>
# 查看线程堆栈
jstack <pid> > thread.txt
# 查看JVM参数
jinfo -flags <pid>
# 查看类加载
jstat -class <pid>
# CPU高使用率排查
top -H -p <pid>
printf '%x' <tid>
jstack <pid> | grep <hex-tid>
# 强制GC
jcmd <pid> GC.run
最佳实践总结
- 不要过早优化 - 先保证功能正确
- 基于数据调优 - 使用监控数据指导
- 逐步调整 - 每次只改一个参数
- 生产验证 - 压测环境验证后再上生产
- 持续监控 - 调优后持续观察效果
相关资源
- 官方文档: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/
- GCEasy: https://gceasy.io
- MAT: https://eclipse.dev/mat/
- Arthas: https://arthas.aliyun.com
- VisualVM: https://visualvm.github.io