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圣经