12.1 JVM性能调优
面试重要程度:⭐⭐⭐⭐⭐
常见提问方式: "如何排查内存泄漏?" "GC调优有哪些策略?" "线上JVM参数如何设置?"
预计阅读时间:35分钟
📊 JVM内存模型与性能分析
JVM内存结构详解
/**
* JVM内存区域分析工具
*/
public class JVMMemoryAnalyzer {
/**
* 内存区域枚举
*/
public enum MemoryArea {
HEAP("堆内存", "存储对象实例,GC主要区域"),
METHOD_AREA("方法区", "存储类信息、常量池、静态变量"),
STACK("虚拟机栈", "存储局部变量、操作数栈"),
PC_REGISTER("程序计数器", "当前线程执行字节码位置"),
NATIVE_STACK("本地方法栈", "Native方法调用栈"),
DIRECT_MEMORY("直接内存", "NIO、Netty等使用的堆外内存");
private final String name;
private final String description;
MemoryArea(String name, String description) {
this.name = name;
this.description = description;
}
}
/**
* 获取JVM内存信息
*/
public static void analyzeMemoryUsage() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
// 堆内存使用情况
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("=== 堆内存使用情况 ===");
System.out.println("初始大小: " + formatBytes(heapUsage.getInit()));
System.out.println("已使用: " + formatBytes(heapUsage.getUsed()));
System.out.println("已提交: " + formatBytes(heapUsage.getCommitted()));
System.out.println("最大值: " + formatBytes(heapUsage.getMax()));
// 非堆内存使用情况
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("\n=== 非堆内存使用情况 ===");
System.out.println("已使用: " + formatBytes(nonHeapUsage.getUsed()));
System.out.println("已提交: " + formatBytes(nonHeapUsage.getCommitted()));
// GC信息
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
System.out.println("\n=== GC统计信息 ===");
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println(gcBean.getName() + ":");
System.out.println(" 收集次数: " + gcBean.getCollectionCount());
System.out.println(" 收集时间: " + gcBean.getCollectionTime() + "ms");
}
}
private static String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
}
}
🔍 内存泄漏排查实战
内存泄漏检测工具
/**
* 内存泄漏检测与分析
*/
public class MemoryLeakDetector {
/**
* 常见内存泄漏场景
*/
public enum LeakType {
STATIC_COLLECTION("静态集合持有对象引用"),
LISTENER_NOT_REMOVED("监听器未正确移除"),
THREAD_LOCAL_NOT_CLEAN("ThreadLocal未清理"),
CONNECTION_NOT_CLOSED("数据库连接未关闭"),
CACHE_UNLIMITED_GROWTH("缓存无限增长"),
INNER_CLASS_REFERENCE("内部类持有外部类引用");
private final String description;
LeakType(String description) {
this.description = description;
}
}
/**
* 模拟内存泄漏场景1:静态集合
*/
public static class StaticCollectionLeak {
// 危险:静态集合持续增长
private static final List<Object> STATIC_LIST = new ArrayList<>();
public void addToStaticList(Object obj) {
STATIC_LIST.add(obj); // 对象永远不会被GC
}
// 修复方案:定期清理或使用WeakReference
private static final Map<String, WeakReference<Object>> WEAK_CACHE =
new ConcurrentHashMap<>();
public void addToWeakCache(String key, Object obj) {
WEAK_CACHE.put(key, new WeakReference<>(obj));
}
}
/**
* 模拟内存泄漏场景2:ThreadLocal未清理
*/
public static class ThreadLocalLeak {
private static final ThreadLocal<List<Object>> THREAD_LOCAL_LIST =
ThreadLocal.withInitial(ArrayList::new);
public void addToThreadLocal(Object obj) {
THREAD_LOCAL_LIST.get().add(obj);
// 危险:未调用remove()
}
// 修复方案:使用try-finally确保清理
public void safeThreadLocalUsage(Object obj) {
try {
THREAD_LOCAL_LIST.get().add(obj);
// 业务逻辑
} finally {
THREAD_LOCAL_LIST.remove(); // 关键:清理ThreadLocal
}
}
}
/**
* 内存使用监控
*/
public static class MemoryMonitor {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void startMonitoring() {
scheduler.scheduleAtFixedRate(() -> {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
double usagePercent = (double) usedMemory / maxMemory * 100;
System.out.printf("内存使用率: %.2f%% (%s/%s)%n",
usagePercent,
formatBytes(usedMemory),
formatBytes(maxMemory));
// 内存使用率超过80%时告警
if (usagePercent > 80) {
System.err.println("警告:内存使用率过高!");
// 可以触发堆转储分析
dumpHeap();
}
}, 0, 30, TimeUnit.SECONDS);
}
private void dumpHeap() {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
String fileName = "heap-dump-" + System.currentTimeMillis() + ".hprof";
mxBean.dumpHeap(fileName, true);
System.out.println("堆转储文件已生成: " + fileName);
} catch (Exception e) {
System.err.println("生成堆转储失败: " + e.getMessage());
}
}
private String formatBytes(long bytes) {
if (bytes < 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
}
}
}
🗑️ GC调优实战案例
垃圾收集器选择与配置
/**
* GC调优策略分析
*/
public class GCTuningStrategy {
/**
* 垃圾收集器类型
*/
public enum GCType {
SERIAL_GC("Serial GC", "单线程,适合小型应用", "-XX:+UseSerialGC"),
PARALLEL_GC("Parallel GC", "多线程,适合吞吐量优先", "-XX:+UseParallelGC"),
CMS_GC("CMS GC", "并发收集,适合低延迟", "-XX:+UseConcMarkSweepGC"),
G1_GC("G1 GC", "低延迟,适合大堆内存", "-XX:+UseG1GC"),
ZGC("ZGC", "超低延迟,JDK11+", "-XX:+UseZGC"),
SHENANDOAH("Shenandoah", "低延迟,JDK12+", "-XX:+UseShenandoahGC");
private final String name;
private final String description;
private final String jvmFlag;
GCType(String name, String description, String jvmFlag) {
this.name = name;
this.description = description;
this.jvmFlag = jvmFlag;
}
}
/**
* G1GC调优参数配置
*/
public static class G1GCTuning {
public static final String[] BASIC_G1_PARAMS = {
"-XX:+UseG1GC", // 启用G1GC
"-XX:MaxGCPauseMillis=200", // 最大GC暂停时间200ms
"-XX:G1HeapRegionSize=16m", // 每个Region大小16MB
"-XX:G1NewSizePercent=30", // 新生代占堆的30%
"-XX:G1MaxNewSizePercent=40", // 新生代最大占堆的40%
"-XX:G1MixedGCCountTarget=8", // Mixed GC次数目标
"-XX:InitiatingHeapOccupancyPercent=45", // 堆占用45%时触发并发标记
"-XX:G1MixedGCLiveThresholdPercent=85" // Region中85%对象存活才参与Mixed GC
};
/**
* 根据应用特征推荐G1参数
*/
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
查看9道真题和解析