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

全部评论

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务