本章概述

内存分析是 Java 应用性能调优的核心环节之一。本章将深入探讨如何使用 VisualVM 进行内存分析,包括堆内存监控、堆转储生成与分析、内存泄漏检测以及内存优化策略。通过本章的学习,你将掌握识别和解决内存相关问题的专业技能。

学习目标

  • 理解 Java 内存模型和堆内存结构
  • 掌握 VisualVM 内存监控功能的使用
  • 学会生成和分析堆转储文件
  • 能够检测和诊断内存泄漏问题
  • 掌握内存优化的最佳实践

1. Java 内存模型基础

1.1 JVM 内存结构

┌─────────────────────────────────────────────────────────────┐
│                        JVM 内存结构                          │
├─────────────────────────────────────────────────────────────┤
│  堆内存 (Heap Memory)                                       │
│  ┌─────────────────┬─────────────────────────────────────┐  │
│  │   年轻代 (Young) │        老年代 (Old/Tenured)         │  │
│  │  ┌─────┬────────┤                                     │  │
│  │  │Eden │Survivor│                                     │  │
│  │  │     │ S0│S1  │                                     │  │
│  │  └─────┴────────┤                                     │  │
│  └─────────────────┴─────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│  非堆内存 (Non-Heap Memory)                                 │
│  ┌─────────────────┬─────────────────┬─────────────────┐  │
│  │   方法区         │   代码缓存       │   压缩类空间     │  │
│  │  (Method Area)  │ (Code Cache)    │(Compressed Class)│  │
│  └─────────────────┴─────────────────┴─────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│  直接内存 (Direct Memory)                                   │
└─────────────────────────────────────────────────────────────┘

1.2 内存区域详解

1.2.1 堆内存 (Heap Memory)

堆内存是 Java 对象实例存储的主要区域:

// 内存区域示例代码
public class MemoryRegionsExample {
    
    // 静态变量存储在方法区
    private static final String STATIC_STRING = "Static String";
    private static List<Object> staticList = new ArrayList<>();
    
    // 实例变量存储在堆内存中
    private String instanceString;
    private List<Object> instanceList;
    
    public MemoryRegionsExample(String str) {
        // 对象实例存储在堆内存
        this.instanceString = str;  // 字符串对象在堆中
        this.instanceList = new ArrayList<>();  // ArrayList 对象在堆中
    }
    
    public void demonstrateMemoryAllocation() {
        // 局部变量引用存储在栈中,对象实例存储在堆中
        String localString = "Local String";  // 字符串常量池
        StringBuilder sb = new StringBuilder();  // StringBuilder 对象在堆中
        
        // 基本类型局部变量存储在栈中
        int localInt = 42;
        long localLong = 123456789L;
        
        // 数组对象存储在堆中
        int[] localArray = new int[1000];
        Object[] objectArray = new Object[100];
        
        // 循环创建对象(年轻代分配)
        for (int i = 0; i < 1000; i++) {
            // 短生命周期对象,通常在年轻代分配和回收
            String tempString = "Temp " + i;
            Object tempObject = new Object();
            
            // 添加到集合中的对象可能晋升到老年代
            if (i % 10 == 0) {
                instanceList.add(tempObject);
            }
        }
    }
    
    public static void demonstrateStaticMemory() {
        // 静态变量操作,影响方法区内存
        for (int i = 0; i < 100; i++) {
            staticList.add(new Object());
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Memory Regions Demonstration ===");
        
        // 创建实例(堆内存分配)
        MemoryRegionsExample example = new MemoryRegionsExample("Example Instance");
        
        // 演示内存分配
        example.demonstrateMemoryAllocation();
        
        // 演示静态内存使用
        demonstrateStaticMemory();
        
        // 打印内存使用情况
        printMemoryUsage();
    }
    
    private static void printMemoryUsage() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();
        
        System.out.println("\n=== Memory Usage ===");
        System.out.printf("Used Memory: %.2f MB%n", usedMemory / 1024.0 / 1024.0);
        System.out.printf("Free Memory: %.2f MB%n", freeMemory / 1024.0 / 1024.0);
        System.out.printf("Total Memory: %.2f MB%n", totalMemory / 1024.0 / 1024.0);
        System.out.printf("Max Memory: %.2f MB%n", maxMemory / 1024.0 / 1024.0);
        System.out.printf("Memory Usage: %.2f%%%n", 
            (double) usedMemory / maxMemory * 100);
    }
}

1.2.2 垃圾回收机制

// 垃圾回收演示
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.*;

public class GarbageCollectionDemo {
    
    private static final List<Object> longLivedObjects = new ArrayList<>();
    private static volatile boolean running = true;
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Garbage Collection Demonstration ===");
        System.out.println("观察不同类型的对象分配和垃圾回收行为");
        
        // 启动内存监控线程
        Thread monitorThread = new Thread(GarbageCollectionDemo::monitorMemory);
        monitorThread.setDaemon(true);
        monitorThread.start();
        
        // 启动 GC 统计线程
        Thread gcStatsThread = new Thread(GarbageCollectionDemo::monitorGC);
        gcStatsThread.setDaemon(true);
        gcStatsThread.start();
        
        // 场景1:大量短生命周期对象(触发年轻代 GC)
        System.out.println("\n场景1:创建大量短生命周期对象...");
        createShortLivedObjects();
        
        Thread.sleep(2000);
        
        // 场景2:长生命周期对象(可能晋升到老年代)
        System.out.println("\n场景2:创建长生命周期对象...");
        createLongLivedObjects();
        
        Thread.sleep(2000);
        
        // 场景3:大对象分配(可能直接进入老年代)
        System.out.println("\n场景3:创建大对象...");
        createLargeObjects();
        
        Thread.sleep(2000);
        
        // 场景4:内存压力测试(触发 Full GC)
        System.out.println("\n场景4:内存压力测试...");
        createMemoryPressure();
        
        Thread.sleep(5000);
        
        // 手动触发 GC
        System.out.println("\n手动触发垃圾回收...");
        System.gc();
        
        Thread.sleep(2000);
        
        running = false;
        System.out.println("\n演示完成!请在 VisualVM 中观察内存使用和 GC 活动");
    }
    
    private static void createShortLivedObjects() {
        for (int i = 0; i < 100000; i++) {
            // 创建短生命周期对象
            String temp = "Short lived object " + i;
            List<Integer> tempList = new ArrayList<>();
            for (int j = 0; j < 10; j++) {
                tempList.add(j);
            }
            
            // 偶尔创建一些稍大的对象
            if (i % 1000 == 0) {
                byte[] tempArray = new byte[1024]; // 1KB
                Arrays.fill(tempArray, (byte) i);
            }
        }
    }
    
    private static void createLongLivedObjects() {
        for (int i = 0; i < 1000; i++) {
            // 创建长生命周期对象并保持引用
            Map<String, Object> longLivedMap = new HashMap<>();
            longLivedMap.put("id", i);
            longLivedMap.put("data", new byte[1024]); // 1KB 数据
            longLivedMap.put("timestamp", System.currentTimeMillis());
            
            longLivedObjects.add(longLivedMap);
            
            // 控制创建速度
            if (i % 100 == 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
    
    private static void createLargeObjects() {
        for (int i = 0; i < 10; i++) {
            // 创建大对象(可能直接分配到老年代)
            byte[] largeArray = new byte[1024 * 1024]; // 1MB
            Arrays.fill(largeArray, (byte) (i % 256));
            
            // 保持一些大对象的引用
            if (i % 3 == 0) {
                longLivedObjects.add(largeArray);
            }
            
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    private static void createMemoryPressure() {
        List<Object> pressureObjects = new ArrayList<>();
        
        try {
            for (int i = 0; i < 1000; i++) {
                // 创建中等大小的对象增加内存压力
                byte[] pressureArray = new byte[512 * 1024]; // 512KB
                Arrays.fill(pressureArray, (byte) (i % 256));
                pressureObjects.add(pressureArray);
                
                // 检查内存使用情况
                if (i % 100 == 0) {
                    Runtime runtime = Runtime.getRuntime();
                    long usedMemory = runtime.totalMemory() - runtime.freeMemory();
                    long maxMemory = runtime.maxMemory();
                    double memoryUsage = (double) usedMemory / maxMemory;
                    
                    System.out.printf("Memory pressure iteration %d: %.2f%% used%n", 
                        i, memoryUsage * 100);
                    
                    // 如果内存使用率过高,停止创建
                    if (memoryUsage > 0.8) {
                        System.out.println("High memory usage detected, stopping pressure test");
                        break;
                    }
                }
                
                Thread.sleep(10);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (OutOfMemoryError e) {
            System.out.println("OutOfMemoryError caught during pressure test");
        }
    }
    
    private static void monitorMemory() {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        
        while (running) {
            try {
                MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
                MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
                
                System.out.printf("[Memory] Heap: %d/%d MB, Non-Heap: %d/%d MB%n",
                    heapUsage.getUsed() / 1024 / 1024,
                    heapUsage.getMax() / 1024 / 1024,
                    nonHeapUsage.getUsed() / 1024 / 1024,
                    nonHeapUsage.getMax() / 1024 / 1024);
                
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    private static void monitorGC() {
        List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
        Map<String, Long> lastCollectionCounts = new HashMap<>();
        Map<String, Long> lastCollectionTimes = new HashMap<>();
        
        // 初始化
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            lastCollectionCounts.put(gcBean.getName(), gcBean.getCollectionCount());
            lastCollectionTimes.put(gcBean.getName(), gcBean.getCollectionTime());
        }
        
        while (running) {
            try {
                Thread.sleep(3000);
                
                for (GarbageCollectorMXBean gcBean : gcBeans) {
                    String gcName = gcBean.getName();
                    long currentCount = gcBean.getCollectionCount();
                    long currentTime = gcBean.getCollectionTime();
                    
                    long lastCount = lastCollectionCounts.get(gcName);
                    long lastTime = lastCollectionTimes.get(gcName);
                    
                    if (currentCount > lastCount) {
                        long collections = currentCount - lastCount;
                        long time = currentTime - lastTime;
                        
                        System.out.printf("[GC] %s: %d collections, %d ms total time%n",
                            gcName, collections, time);
                        
                        lastCollectionCounts.put(gcName, currentCount);
                        lastCollectionTimes.put(gcName, currentTime);
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

2. VisualVM 内存监控

2.1 Monitor 标签页内存监控

2.1.1 内存监控界面

VisualVM 的 Monitor 标签页提供了实时的内存使用情况监控:

┌─────────────────────────────────────────────────────────────┐
│                    Memory 监控面板                          │
├─────────────────────────────────────────────────────────────┤
│  堆内存使用情况                                              │
│  ┌─────────────────────────────────────────────────────┐  │
│  │     Used: 245 MB    Size: 512 MB    Max: 1024 MB   │  │
│  │  ████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  │  │
│  │                    48% 使用率                       │  │
│  └─────────────────────────────────────────────────────┘  │
│                                                             │
│  非堆内存使用情况                                            │
│  ┌─────────────────────────────────────────────────────┐  │
│  │     Used: 45 MB     Size: 64 MB     Max: 256 MB    │  │
│  │  ████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░  │  │
│  │                    70% 使用率                       │  │
│  └─────────────────────────────────────────────────────┘  │
│                                                             │
│  [Perform GC] [Heap Dump]                                  │
└─────────────────────────────────────────────────────────────┘

2.1.2 内存监控指标解读

// 内存监控指标解读示例
import java.lang.management.*;
import java.util.*;

public class MemoryMonitoringMetrics {
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Memory Monitoring Metrics ===");
        
        // 启动内存指标监控
        MemoryMetricsCollector collector = new MemoryMetricsCollector();
        collector.startMonitoring();
        
        // 模拟不同的内存使用模式
        simulateMemoryUsagePatterns();
        
        collector.stopMonitoring();
        collector.printSummary();
    }
    
    private static void simulateMemoryUsagePatterns() throws InterruptedException {
        System.out.println("\n开始模拟不同的内存使用模式...");
        
        // 模式1:稳定增长
        System.out.println("模式1:稳定内存增长");
        List<Object> steadyGrowth = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            steadyGrowth.add(new byte[1024]); // 1KB per object
            if (i % 100 == 0) {
                Thread.sleep(100);
            }
        }
        
        Thread.sleep(2000);
        
        // 模式2:锯齿状(分配后释放)
        System.out.println("模式2:锯齿状内存使用");
        for (int cycle = 0; cycle < 5; cycle++) {
            List<Object> tempObjects = new ArrayList<>();
            
            // 快速分配
            for (int i = 0; i < 500; i++) {
                tempObjects.add(new byte[2048]); // 2KB per object
            }
            
            Thread.sleep(1000);
            
            // 释放引用(等待 GC)
            tempObjects.clear();
            System.gc();
            
            Thread.sleep(1000);
        }
        
        // 模式3:内存泄漏模拟
        System.out.println("模式3:内存泄漏模拟");
        simulateMemoryLeak();
    }
    
    private static void simulateMemoryLeak() throws InterruptedException {
        // 模拟内存泄漏:持续增长且不释放
        List<Object> leakyList = new ArrayList<>();
        
        for (int i = 0; i < 2000; i++) {
            // 创建包含循环引用的对象
            LeakyObject obj = new LeakyObject("Object " + i);
            obj.setReference(obj); // 自引用
            leakyList.add(obj);
            
            if (i % 200 == 0) {
                Thread.sleep(100);
                System.out.println("Created " + (i + 1) + " leaky objects");
            }
        }
    }
    
    // 模拟内存泄漏的对象
    static class LeakyObject {
        private String data;
        private byte[] payload;
        private LeakyObject reference;
        
        public LeakyObject(String data) {
            this.data = data;
            this.payload = new byte[1024]; // 1KB payload
        }
        
        public void setReference(LeakyObject reference) {
            this.reference = reference;
        }
    }
    
    // 内存指标收集器
    static class MemoryMetricsCollector {
        private final MemoryMXBean memoryBean;
        private final List<GarbageCollectorMXBean> gcBeans;
        private final Timer timer;
        private final List<MemorySnapshot> snapshots;
        private volatile boolean monitoring;
        
        public MemoryMetricsCollector() {
            this.memoryBean = ManagementFactory.getMemoryMXBean();
            this.gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
            this.timer = new Timer("MemoryMonitor", true);
            this.snapshots = new ArrayList<>();
        }
        
        public void startMonitoring() {
            monitoring = true;
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    if (monitoring) {
                        collectSnapshot();
                    }
                }
            }, 0, 1000); // 每秒收集一次
        }
        
        public void stopMonitoring() {
            monitoring = false;
            timer.cancel();
        }
        
        private void collectSnapshot() {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
            
            long totalGcCount = 0;
            long totalGcTime = 0;
            
            for (GarbageCollectorMXBean gcBean : gcBeans) {
                totalGcCount += gcBean.getCollectionCount();
                totalGcTime += gcBean.getCollectionTime();
            }
            
            MemorySnapshot snapshot = new MemorySnapshot(
                System.currentTimeMillis(),
                heapUsage.getUsed(),
                heapUsage.getCommitted(),
                heapUsage.getMax(),
                nonHeapUsage.getUsed(),
                nonHeapUsage.getCommitted(),
                totalGcCount,
                totalGcTime
            );
            
            snapshots.add(snapshot);
            
            // 实时输出关键指标
            double heapUsagePercent = (double) heapUsage.getUsed() / heapUsage.getMax() * 100;
            if (heapUsagePercent > 80) {
                System.out.printf("⚠️  High heap usage: %.1f%%%n", heapUsagePercent);
            }
        }
        
        public void printSummary() {
            if (snapshots.isEmpty()) {
                System.out.println("No monitoring data collected");
                return;
            }
            
            System.out.println("\n=== Memory Monitoring Summary ===");
            
            MemorySnapshot first = snapshots.get(0);
            MemorySnapshot last = snapshots.get(snapshots.size() - 1);
            
            System.out.printf("Monitoring duration: %.1f seconds%n", 
                (last.timestamp - first.timestamp) / 1000.0);
            System.out.printf("Total snapshots: %d%n", snapshots.size());
            
            // 堆内存统计
            long maxHeapUsed = snapshots.stream()
                .mapToLong(s -> s.heapUsed)
                .max().orElse(0);
            long minHeapUsed = snapshots.stream()
                .mapToLong(s -> s.heapUsed)
                .min().orElse(0);
            double avgHeapUsed = snapshots.stream()
                .mapToLong(s -> s.heapUsed)
                .average().orElse(0);
            
            System.out.printf("\nHeap Memory Usage:%n");
            System.out.printf("  Min: %.2f MB%n", minHeapUsed / 1024.0 / 1024.0);
            System.out.printf("  Max: %.2f MB%n", maxHeapUsed / 1024.0 / 1024.0);
            System.out.printf("  Avg: %.2f MB%n", avgHeapUsed / 1024.0 / 1024.0);
            
            // GC 统计
            long gcCountDiff = last.totalGcCount - first.totalGcCount;
            long gcTimeDiff = last.totalGcTime - first.totalGcTime;
            
            System.out.printf("\nGarbage Collection:%n");
            System.out.printf("  Total collections: %d%n", gcCountDiff);
            System.out.printf("  Total GC time: %d ms%n", gcTimeDiff);
            
            if (gcCountDiff > 0) {
                System.out.printf("  Average GC time: %.2f ms%n", 
                    (double) gcTimeDiff / gcCountDiff);
            }
            
            // 内存增长趋势
            long heapGrowth = last.heapUsed - first.heapUsed;
            System.out.printf("\nMemory Growth:%n");
            System.out.printf("  Heap growth: %.2f MB%n", heapGrowth / 1024.0 / 1024.0);
            
            if (heapGrowth > 0) {
                double growthRate = (double) heapGrowth / (last.timestamp - first.timestamp) * 1000;
                System.out.printf("  Growth rate: %.2f MB/s%n", growthRate / 1024.0 / 1024.0);
            }
        }
    }
    
    // 内存快照数据结构
    static class MemorySnapshot {
        final long timestamp;
        final long heapUsed;
        final long heapCommitted;
        final long heapMax;
        final long nonHeapUsed;
        final long nonHeapCommitted;
        final long totalGcCount;
        final long totalGcTime;
        
        public MemorySnapshot(long timestamp, long heapUsed, long heapCommitted, 
                            long heapMax, long nonHeapUsed, long nonHeapCommitted,
                            long totalGcCount, long totalGcTime) {
            this.timestamp = timestamp;
            this.heapUsed = heapUsed;
            this.heapCommitted = heapCommitted;
            this.heapMax = heapMax;
            this.nonHeapUsed = nonHeapUsed;
            this.nonHeapCommitted = nonHeapCommitted;
            this.totalGcCount = totalGcCount;
            this.totalGcTime = totalGcTime;
        }
    }
}

2.2 内存使用模式分析

2.2.1 正常内存使用模式

内存使用量
    ↑
    │     ╭─╮     ╭─╮     ╭─╮
    │    ╱   ╲   ╱   ╲   ╱   ╲
    │   ╱     ╲ ╱     ╲ ╱     ╲
    │  ╱       ╲╱       ╲╱       ╲
    │ ╱                           ╲
    └─────────────────────────────────→ 时间
      分配  GC  分配  GC  分配  GC
      
特征:
- 锯齿状模式
- 定期的内存回收
- 内存使用在合理范围内波动

2.2.2 内存泄漏模式

内存使用量
    ↑
    │                           ╱
    │                       ╱╱╱
    │                   ╱╱╱
    │               ╱╱╱
    │           ╱╱╱
    │       ╱╱╱
    │   ╱╱╱
    │╱╱╱
    └─────────────────────────────────→ 时间
      
特征:
- 持续上升趋势
- GC 后内存不能有效回收
- 最终可能导致 OutOfMemoryError

3. 堆转储生成与分析

3.1 堆转储生成方法

3.1.1 通过 VisualVM 生成

在 VisualVM 中生成堆转储的步骤:

  1. 连接到目标 Java 应用程序
  2. 切换到 Monitor 标签页
  3. 点击 “Heap Dump” 按钮
  4. 等待堆转储生成完成
  5. 自动打开堆转储分析视图

3.1.2 程序化生成堆转储

// 程序化生成堆转储
import com.sun.management.HotSpotDiagnosticMXBean;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.management.MBeanServer;

public class HeapDumpGenerator {
    
    private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
    
    public static void generateHeapDump(String filename, boolean live) {
        try {
            MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            HotSpotDiagnosticMXBean hotspotBean = ManagementFactory.newPlatformMXBeanProxy(
                server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class);
            
            System.out.println("Generating heap dump: " + filename);
            long startTime = System.currentTimeMillis();
            
            hotspotBean.dumpHeap(filename, live);
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.printf("Heap dump generated successfully in %d ms%n", duration);
            
        } catch (IOException e) {
            System.err.println("Failed to generate heap dump: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    public static void generateTimestampedHeapDump(boolean live) {
        String timestamp = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")
            .format(LocalDateTime.now());
        String filename = "heap_dump_" + timestamp + ".hprof";
        generateHeapDump(filename, live);
    }
    
    public static void generateHeapDumpOnOutOfMemory() {
        // 这个方法演示如何在程序中设置 OOM 时自动生成堆转储
        // 实际应该通过 JVM 参数设置:
        // -XX:+HeapDumpOnOutOfMemoryError
        // -XX:HeapDumpPath=/path/to/dumps/
        
        System.out.println("To enable automatic heap dump on OOM, use JVM parameters:");
        System.out.println("-XX:+HeapDumpOnOutOfMemoryError");
        System.out.println("-XX:HeapDumpPath=/path/to/dumps/");
    }
    
    public static void main(String[] args) {
        System.out.println("=== Heap Dump Generator ===");
        
        // 创建一些对象用于演示
        createSampleObjects();
        
        // 生成堆转储(包含所有对象)
        System.out.println("\n生成完整堆转储(包含所有对象)...");
        generateTimestampedHeapDump(false);
        
        // 触发 GC
        System.out.println("\n触发垃圾回收...");
        System.gc();
        
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // 生成活动对象堆转储(仅包含存活对象)
        System.out.println("\n生成活动对象堆转储(仅包含存活对象)...");
        generateTimestampedHeapDump(true);
        
        System.out.println("\n堆转储生成完成!");
        System.out.println("请使用 VisualVM 或其他工具分析生成的 .hprof 文件");
    }
    
    private static void createSampleObjects() {
        System.out.println("创建示例对象...");
        
        // 创建各种类型的对象用于堆转储分析
        
        // 1. 字符串对象
        for (int i = 0; i < 1000; i++) {
            String str = "Sample String " + i;
        }
        
        // 2. 集合对象
        java.util.List<Object> list = new java.util.ArrayList<>();
        for (int i = 0; i < 500; i++) {
            list.add(new SampleObject("Object " + i, i));
        }
        
        // 3. 数组对象
        int[] intArray = new int[10000];
        for (int i = 0; i < intArray.length; i++) {
            intArray[i] = i;
        }
        
        // 4. 自定义对象
        SampleObject[] objectArray = new SampleObject[100];
        for (int i = 0; i < objectArray.length; i++) {
            objectArray[i] = new SampleObject("Array Object " + i, i);
        }
        
        System.out.println("示例对象创建完成");
    }
    
    // 示例对象类
    static class SampleObject {
        private String name;
        private int value;
        private byte[] data;
        
        public SampleObject(String name, int value) {
            this.name = name;
            this.value = value;
            this.data = new byte[1024]; // 1KB 数据
            java.util.Arrays.fill(data, (byte) (value % 256));
        }
        
        @Override
        public String toString() {
            return String.format("SampleObject{name='%s', value=%d}", name, value);
        }
    }
}

3.1.3 命令行生成堆转储

# 使用 jcmd 生成堆转储
jcmd <pid> GC.run_finalization
jcmd <pid> VM.gc
jcmd <pid> GC.dump_heap /path/to/heap_dump.hprof

# 使用 jmap 生成堆转储
jmap -dump:live,format=b,file=/path/to/heap_dump.hprof <pid>

# 使用 jmap 生成所有对象的堆转储
jmap -dump:format=b,file=/path/to/heap_dump_all.hprof <pid>

3.2 堆转储分析

3.2.1 VisualVM 堆转储分析界面

┌─────────────────────────────────────────────────────────────┐
│                    Heap Dump 分析界面                       │
├─────────────────────────────────────────────────────────────┤
│  Summary | Classes | Instances | OQL Console                │
├─────────────────────────────────────────────────────────────┤
│  Summary 标签页:                                           │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  Environment:                                       │  │
│  │    JVM: OpenJDK 64-Bit Server VM 11.0.16           │  │
│  │    OS: Windows 10 x64                               │  │
│  │    Date: 2024-01-15 14:30:45                        │  │
│  │                                                     │  │
│  │  Basic Info:                                        │  │
│  │    Total Size: 245.6 MB                             │  │
│  │    Total Classes: 1,234                             │  │
│  │    Total Instances: 1,234,567                       │  │
│  │    Total Class Loaders: 45                          │  │
│  │                                                     │  │
│  │  Largest Objects:                                   │  │
│  │    java.lang.Object[]     45.2 MB (18.4%)          │  │
│  │    java.lang.String       23.1 MB (9.4%)           │  │
│  │    char[]                 18.7 MB (7.6%)           │  │
│  │    byte[]                 15.3 MB (6.2%)           │  │
│  └─────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

3.2.2 自动化堆转储分析工具

// 堆转储自动分析工具
import java.io.*;
import java.util.*;
import java.util.regex.*;

public class HeapDumpAnalyzer {
    
    public static class HeapAnalysisResult {
        public long totalSize;
        public int totalClasses;
        public long totalInstances;
        public Map<String, ClassInfo> classInfoMap;
        public List<String> suspiciousPatterns;
        public Map<String, Long> largestObjects;
        
        public HeapAnalysisResult() {
            classInfoMap = new HashMap<>();
            suspiciousPatterns = new ArrayList<>();
            largestObjects = new LinkedHashMap<>();
        }
        
        public void printSummary() {
            System.out.println("=== Heap Dump Analysis Summary ===");
            System.out.printf("Total heap size: %.2f MB%n", totalSize / 1024.0 / 1024.0);
            System.out.printf("Total classes: %d%n", totalClasses);
            System.out.printf("Total instances: %,d%n", totalInstances);
            
            System.out.println("\n🔍 Top classes by memory usage:");
            classInfoMap.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue().totalSize, e1.getValue().totalSize))
                .limit(10)
                .forEach(entry -> {
                    ClassInfo info = entry.getValue();
                    double percentage = (double) info.totalSize / totalSize * 100;
                    System.out.printf("  %-40s %,8d instances  %8.2f MB (%5.2f%%)%n",
                        entry.getKey(), info.instanceCount, 
                        info.totalSize / 1024.0 / 1024.0, percentage);
                });
            
            if (!suspiciousPatterns.isEmpty()) {
                System.out.println("\n⚠️  Suspicious patterns detected:");
                suspiciousPatterns.forEach(pattern -> 
                    System.out.println("  " + pattern));
            }
            
            System.out.println("\n📊 Largest individual objects:");
            largestObjects.entrySet().stream()
                .limit(5)
                .forEach(entry -> 
                    System.out.printf("  %-40s %8.2f MB%n",
                        entry.getKey(), entry.getValue() / 1024.0 / 1024.0));
        }
    }
    
    public static class ClassInfo {
        public long instanceCount;
        public long totalSize;
        public long averageSize;
        
        public ClassInfo(long instanceCount, long totalSize) {
            this.instanceCount = instanceCount;
            this.totalSize = totalSize;
            this.averageSize = instanceCount > 0 ? totalSize / instanceCount : 0;
        }
    }
    
    // 注意:这是一个简化的分析器示例
    // 实际的 .hprof 文件解析需要专门的库,如 Eclipse MAT 的 API
    public static HeapAnalysisResult analyzeHeapDump(String hprofFile) {
        HeapAnalysisResult result = new HeapAnalysisResult();
        
        // 这里演示分析逻辑,实际需要使用专门的 HPROF 解析库
        System.out.println("Analyzing heap dump: " + hprofFile);
        System.out.println("Note: This is a simplified analyzer for demonstration");
        System.out.println("For real analysis, use tools like Eclipse MAT, VisualVM, or JProfiler");
        
        // 模拟分析结果
        simulateAnalysisResults(result);
        
        return result;
    }
    
    private static void simulateAnalysisResults(HeapAnalysisResult result) {
        // 模拟堆转储分析结果
        result.totalSize = 256 * 1024 * 1024; // 256 MB
        result.totalClasses = 1500;
        result.totalInstances = 2000000;
        
        // 模拟类信息
        result.classInfoMap.put("java.lang.String", new ClassInfo(150000, 24 * 1024 * 1024));
        result.classInfoMap.put("char[]", new ClassInfo(150000, 18 * 1024 * 1024));
        result.classInfoMap.put("java.lang.Object[]", new ClassInfo(5000, 45 * 1024 * 1024));
        result.classInfoMap.put("byte[]", new ClassInfo(8000, 16 * 1024 * 1024));
        result.classInfoMap.put("java.util.HashMap$Node", new ClassInfo(80000, 12 * 1024 * 1024));
        result.classInfoMap.put("java.util.ArrayList", new ClassInfo(25000, 8 * 1024 * 1024));
        result.classInfoMap.put("com.example.CustomObject", new ClassInfo(10000, 15 * 1024 * 1024));
        
        // 检测可疑模式
        detectSuspiciousPatterns(result);
        
        // 模拟最大对象
        result.largestObjects.put("Large byte array #1", 8L * 1024 * 1024);
        result.largestObjects.put("Large Object array #1", 6L * 1024 * 1024);
        result.largestObjects.put("Large HashMap #1", 4L * 1024 * 1024);
        result.largestObjects.put("Large String array #1", 3L * 1024 * 1024);
        result.largestObjects.put("Large ArrayList #1", 2L * 1024 * 1024);
    }
    
    private static void detectSuspiciousPatterns(HeapAnalysisResult result) {
        // 检测可疑的内存使用模式
        
        for (Map.Entry<String, ClassInfo> entry : result.classInfoMap.entrySet()) {
            String className = entry.getKey();
            ClassInfo info = entry.getValue();
            
            // 检测大量小对象
            if (info.instanceCount > 100000 && info.averageSize < 100) {
                result.suspiciousPatterns.add(
                    String.format("Large number of small objects: %s (%,d instances, avg %.1f bytes)",
                        className, info.instanceCount, (double) info.averageSize));
            }
            
            // 检测内存占用过大的类
            double percentage = (double) info.totalSize / result.totalSize * 100;
            if (percentage > 20) {
                result.suspiciousPatterns.add(
                    String.format("Class consuming large memory: %s (%.1f%% of total heap)",
                        className, percentage));
            }
            
            // 检测可能的内存泄漏
            if (className.contains("Cache") || className.contains("Pool")) {
                if (info.instanceCount > 10000) {
                    result.suspiciousPatterns.add(
                        String.format("Potential memory leak in cache/pool: %s (%,d instances)",
                            className, info.instanceCount));
                }
            }
        }
        
        // 检测字符串重复
        ClassInfo stringInfo = result.classInfoMap.get("java.lang.String");
        ClassInfo charArrayInfo = result.classInfoMap.get("char[]");
        
        if (stringInfo != null && charArrayInfo != null) {
            if (stringInfo.instanceCount > 50000) {
                result.suspiciousPatterns.add(
                    String.format("Large number of String objects: %,d instances (consider string interning)",
                        stringInfo.instanceCount));
            }
        }
    }
    
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java HeapDumpAnalyzer <heap_dump.hprof>");
            System.out.println("\nNote: This is a demonstration analyzer.");
            System.out.println("For production use, consider:");
            System.out.println("  - Eclipse Memory Analyzer (MAT)");
            System.out.println("  - VisualVM");
            System.out.println("  - JProfiler");
            System.out.println("  - JConsole");
            return;
        }
        
        String hprofFile = args[0];
        
        if (!new File(hprofFile).exists()) {
            System.err.println("Heap dump file not found: " + hprofFile);
            return;
        }
        
        HeapAnalysisResult result = analyzeHeapDump(hprofFile);
        result.printSummary();
        
        System.out.println("\n💡 Analysis Tips:");
        System.out.println("  1. Focus on classes with high memory usage");
        System.out.println("  2. Look for unexpected object counts");
        System.out.println("  3. Check for duplicate strings (consider interning)");
        System.out.println("  4. Investigate large arrays and collections");
        System.out.println("  5. Analyze object reference chains for memory leaks");
    }
}

4. 内存泄漏检测

4.1 内存泄漏类型

4.1.1 常见内存泄漏场景

// 内存泄漏示例代码
import java.util.*;
import java.util.concurrent.*;

public class MemoryLeakExamples {
    
    // 1. 静态集合导致的内存泄漏
    private static final List<Object> STATIC_LIST = new ArrayList<>();
    private static final Map<String, Object> STATIC_CACHE = new HashMap<>();
    
    // 2. 监听器未移除导致的内存泄漏
    private final List<EventListener> listeners = new ArrayList<>();
    
    // 3. 线程池未关闭导致的内存泄漏
    private ExecutorService executorService;
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Memory Leak Examples ===");
        System.out.println("演示常见的内存泄漏场景");
        
        MemoryLeakExamples examples = new MemoryLeakExamples();
        
        // 启动内存监控
        MemoryMonitor monitor = new MemoryMonitor();
        monitor.start();
        
        // 演示各种内存泄漏
        examples.demonstrateStaticCollectionLeak();
        Thread.sleep(2000);
        
        examples.demonstrateListenerLeak();
        Thread.sleep(2000);
        
        examples.demonstrateThreadPoolLeak();
        Thread.sleep(2000);
        
        examples.demonstrateInnerClassLeak();
        Thread.sleep(2000);
        
        examples.demonstrateResourceLeak();
        Thread.sleep(2000);
        
        monitor.stop();
        
        System.out.println("\n内存泄漏演示完成!");
        System.out.println("请在 VisualVM 中观察内存使用情况和生成堆转储进行分析");
    }
    
    // 1. 静态集合内存泄漏
    public void demonstrateStaticCollectionLeak() {
        System.out.println("\n1. 演示静态集合内存泄漏...");
        
        for (int i = 0; i < 10000; i++) {
            // 向静态集合添加对象,永远不会被回收
            STATIC_LIST.add(new LargeObject("Static Object " + i));
            STATIC_CACHE.put("key" + i, new LargeObject("Cached Object " + i));
            
            if (i % 1000 == 0) {
                System.out.println("Added " + (i + 1) + " objects to static collections");
            }
        }
        
        System.out.println("Static collections now contain " + STATIC_LIST.size() + " objects");
        System.out.println("⚠️  These objects will never be garbage collected!");
    }
    
    // 2. 监听器内存泄漏
    public void demonstrateListenerLeak() {
        System.out.println("\n2. 演示监听器内存泄漏...");
        
        EventSource eventSource = new EventSource();
        
        for (int i = 0; i < 1000; i++) {
            // 创建监听器但不移除,导致内存泄漏
            EventListener listener = new EventListener() {
                private final LargeObject data = new LargeObject("Listener Data");
                
                @Override
                public void onEvent(String event) {
                    // 处理事件
                }
            };
            
            eventSource.addListener(listener);
            listeners.add(listener);
        }
        
        System.out.println("Created " + listeners.size() + " listeners");
        System.out.println("⚠️  Listeners are not removed, causing memory leak!");
        
        // 正确的做法应该是:
        // eventSource.removeListener(listener);
    }
    
    // 3. 线程池内存泄漏
    public void demonstrateThreadPoolLeak() {
        System.out.println("\n3. 演示线程池内存泄漏...");
        
        for (int pool = 0; pool < 10; pool++) {
            // 创建线程池但不关闭
            ExecutorService executor = Executors.newFixedThreadPool(5);
            
            for (int task = 0; task < 100; task++) {
                final int taskId = task;
                executor.submit(() -> {
                    // 模拟任务执行
                    LargeObject taskData = new LargeObject("Task Data " + taskId);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
            
            // ⚠️  没有调用 executor.shutdown(),导致线程池泄漏
            System.out.println("Created thread pool " + (pool + 1) + " (not shutdown)");
        }
        
        System.out.println("⚠️  Thread pools are not shutdown, causing memory leak!");
    }
    
    // 4. 内部类内存泄漏
    public void demonstrateInnerClassLeak() {
        System.out.println("\n4. 演示内部类内存泄漏...");
        
        List<InnerClassHolder> holders = new ArrayList<>();
        
        for (int i = 0; i < 1000; i++) {
            // 非静态内部类持有外部类引用
            InnerClassHolder holder = new InnerClassHolder();
            InnerClass inner = holder.createInnerClass();
            
            // 只保持内部类引用,但外部类也不会被回收
            holders.add(holder);
        }
        
        System.out.println("Created " + holders.size() + " inner class instances");
        System.out.println("⚠️  Inner classes hold references to outer classes!");
    }
    
    // 5. 资源未关闭导致的内存泄漏
    public void demonstrateResourceLeak() {
        System.out.println("\n5. 演示资源未关闭内存泄漏...");
        
        for (int i = 0; i < 100; i++) {
            try {
                // 创建资源但不关闭
                LeakyResource resource = new LeakyResource("Resource " + i);
                resource.doSomething();
                
                // ⚠️  没有调用 resource.close(),导致资源泄漏
                
            } catch (Exception e) {
                System.err.println("Error creating resource: " + e.getMessage());
            }
        }
        
        System.out.println("Created 100 resources without closing them");
        System.out.println("⚠️  Resources are not closed, causing memory leak!");
    }
    
    // 大对象类
    static class LargeObject {
        private final String name;
        private final byte[] data;
        
        public LargeObject(String name) {
            this.name = name;
            this.data = new byte[10240]; // 10KB
            Arrays.fill(data, (byte) name.hashCode());
        }
        
        @Override
        public String toString() {
            return "LargeObject{name='" + name + "', size=" + data.length + "}";
        }
    }
    
    // 事件监听器接口
    interface EventListener {
        void onEvent(String event);
    }
    
    // 事件源
    static class EventSource {
        private final List<EventListener> listeners = new ArrayList<>();
        
        public void addListener(EventListener listener) {
            listeners.add(listener);
        }
        
        public void removeListener(EventListener listener) {
            listeners.remove(listener);
        }
        
        public void fireEvent(String event) {
            for (EventListener listener : listeners) {
                listener.onEvent(event);
            }
        }
    }
    
    // 内部类持有者
    class InnerClassHolder {
        private final LargeObject data = new LargeObject("Holder Data");
        
        public InnerClass createInnerClass() {
            return new InnerClass();
        }
        
        // 非静态内部类(持有外部类引用)
        class InnerClass {
            private final LargeObject innerData = new LargeObject("Inner Data");
            
            public void doSomething() {
                // 可以访问外部类的成员
                System.out.println("Inner class accessing outer: " + data);
            }
        }
    }
    
    // 泄漏资源类
    static class LeakyResource {
        private final String name;
        private final Timer timer;
        private final LargeObject resource;
        
        public LeakyResource(String name) {
            this.name = name;
            this.resource = new LargeObject("Resource Data");
            this.timer = new Timer("Resource Timer " + name, false);
            
            // 启动定时任务
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    // 模拟资源使用
                }
            }, 0, 1000);
        }
        
        public void doSomething() {
            System.out.println("Using resource: " + name);
        }
        
        // 应该调用但经常被忘记的清理方法
        public void close() {
            if (timer != null) {
                timer.cancel();
            }
        }
    }
    
    // 内存监控器
    static class MemoryMonitor {
        private Timer timer;
        private volatile boolean running;
        
        public void start() {
            running = true;
            timer = new Timer("MemoryMonitor", true);
            
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    if (running) {
                        printMemoryUsage();
                    }
                }
            }, 0, 5000);
        }
        
        public void stop() {
            running = false;
            if (timer != null) {
                timer.cancel();
            }
        }
        
        private void printMemoryUsage() {
            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("[Memory] Used: %.1f MB (%.1f%%), Free: %.1f MB, Total: %.1f MB%n",
                usedMemory / 1024.0 / 1024.0,
                usagePercent,
                freeMemory / 1024.0 / 1024.0,
                totalMemory / 1024.0 / 1024.0);
            
            if (usagePercent > 80) {
                System.out.println("⚠️  High memory usage detected!");
            }
        }
    }
}

4.2 内存泄漏检测策略

4.2.1 使用 VisualVM 检测内存泄漏

检测步骤:

  1. 长期监控:连接应用程序,观察内存使用趋势
  2. 多次 GC:手动触发垃圾回收,观察内存是否下降
  3. 堆转储对比:在不同时间点生成堆转储,对比分析
  4. 对象增长分析:关注持续增长的对象类型

4.2.2 内存泄漏检测工具

// 内存泄漏检测工具
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.*;

public class MemoryLeakDetector {
    
    private final MemoryMXBean memoryBean;
    private final List<GarbageCollectorMXBean> gcBeans;
    private final ScheduledExecutorService scheduler;
    private final List<MemorySnapshot> snapshots;
    private final Map<String, Long> classInstanceCounts;
    
    public MemoryLeakDetector() {
        this.memoryBean = ManagementFactory.getMemoryMXBean();
        this.gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
        this.scheduler = Executors.newScheduledThreadPool(1);
        this.snapshots = new ArrayList<>();
        this.classInstanceCounts = new ConcurrentHashMap<>();
    }
    
    public void startDetection(long intervalSeconds) {
        System.out.println("Starting memory leak detection...");
        
        scheduler.scheduleAtFixedRate(() -> {
            try {
                collectSnapshot();
                analyzeLeakPatterns();
            } catch (Exception e) {
                System.err.println("Error during leak detection: " + e.getMessage());
            }
        }, 0, intervalSeconds, TimeUnit.SECONDS);
    }
    
    public void stopDetection() {
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    
    private void collectSnapshot() {
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        
        long totalGcCount = 0;
        long totalGcTime = 0;
        
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            totalGcCount += gcBean.getCollectionCount();
            totalGcTime += gcBean.getCollectionTime();
        }
        
        MemorySnapshot snapshot = new MemorySnapshot(
            System.currentTimeMillis(),
            heapUsage.getUsed(),
            heapUsage.getCommitted(),
            heapUsage.getMax(),
            totalGcCount,
            totalGcTime
        );
        
        snapshots.add(snapshot);
        
        // 保持最近的 100 个快照
        if (snapshots.size() > 100) {
            snapshots.remove(0);
        }
    }
    
    private void analyzeLeakPatterns() {
        if (snapshots.size() < 10) {
            return; // 需要足够的数据点
        }
        
        // 分析内存增长趋势
        analyzeMemoryTrend();
        
        // 分析 GC 效果
        analyzeGCEffectiveness();
        
        // 检测内存泄漏警告
        detectLeakWarnings();
    }
    
    private void analyzeMemoryTrend() {
        if (snapshots.size() < 5) return;
        
        // 计算最近 5 个快照的内存增长趋势
        List<MemorySnapshot> recent = snapshots.subList(
            Math.max(0, snapshots.size() - 5), snapshots.size());
        
        long startMemory = recent.get(0).heapUsed;
        long endMemory = recent.get(recent.size() - 1).heapUsed;
        long timeDiff = recent.get(recent.size() - 1).timestamp - recent.get(0).timestamp;
        
        if (timeDiff > 0) {
            double growthRate = (double) (endMemory - startMemory) / timeDiff * 1000; // bytes per second
            
            if (growthRate > 1024 * 1024) { // > 1MB/s
                System.out.printf("⚠️  High memory growth rate: %.2f MB/s%n", 
                    growthRate / 1024 / 1024);
            }
        }
    }
    
    private void analyzeGCEffectiveness() {
        if (snapshots.size() < 3) return;
        
        MemorySnapshot current = snapshots.get(snapshots.size() - 1);
        MemorySnapshot previous = snapshots.get(snapshots.size() - 2);
        
        // 检查 GC 是否发生
        if (current.totalGcCount > previous.totalGcCount) {
            long gcCount = current.totalGcCount - previous.totalGcCount;
            long memoryBefore = previous.heapUsed;
            long memoryAfter = current.heapUsed;
            
            if (memoryAfter >= memoryBefore * 0.9) { // GC 后内存只减少了不到 10%
                System.out.printf("⚠️  Ineffective GC: %d collections, memory %.1f MB -> %.1f MB%n",
                    gcCount, 
                    memoryBefore / 1024.0 / 1024.0,
                    memoryAfter / 1024.0 / 1024.0);
            }
        }
    }
    
    private void detectLeakWarnings() {
        if (snapshots.size() < 20) return;
        
        // 检查长期内存增长趋势
        MemorySnapshot start = snapshots.get(0);
        MemorySnapshot end = snapshots.get(snapshots.size() - 1);
        
        double memoryGrowth = (double) (end.heapUsed - start.heapUsed) / start.heapUsed * 100;
        long timeDiff = end.timestamp - start.timestamp;
        
        if (memoryGrowth > 50 && timeDiff > 60000) { // 内存增长超过 50% 且时间超过 1 分钟
            System.out.printf("🚨 MEMORY LEAK WARNING: Memory increased by %.1f%% over %.1f minutes%n",
                memoryGrowth, timeDiff / 60000.0);
            
            // 建议生成堆转储
            System.out.println("💡 Recommendation: Generate heap dump for analysis");
            System.out.println("   Use: jcmd <pid> GC.dump_heap /path/to/heap_dump.hprof");
        }
        
        // 检查内存使用率
        double memoryUsage = (double) end.heapUsed / end.heapMax * 100;
        if (memoryUsage > 85) {
            System.out.printf("🚨 HIGH MEMORY USAGE: %.1f%% of heap used%n", memoryUsage);
        }
    }
    
    public void printDetectionSummary() {
        if (snapshots.isEmpty()) {
            System.out.println("No detection data available");
            return;
        }
        
        System.out.println("\n=== Memory Leak Detection Summary ===");
        
        MemorySnapshot first = snapshots.get(0);
        MemorySnapshot last = snapshots.get(snapshots.size() - 1);
        
        long timeDiff = last.timestamp - first.timestamp;
        long memoryDiff = last.heapUsed - first.heapUsed;
        
        System.out.printf("Detection period: %.1f minutes%n", timeDiff / 60000.0);
        System.out.printf("Memory change: %+.2f MB%n", memoryDiff / 1024.0 / 1024.0);
        System.out.printf("GC collections: %d%n", last.totalGcCount - first.totalGcCount);
        System.out.printf("GC time: %d ms%n", last.totalGcTime - first.totalGcTime);
        
        // 计算平均内存使用率
        double avgMemoryUsage = snapshots.stream()
            .mapToDouble(s -> (double) s.heapUsed / s.heapMax * 100)
            .average().orElse(0);
        
        System.out.printf("Average memory usage: %.1f%%%n", avgMemoryUsage);
        
        // 检测结果
        if (memoryDiff > 50 * 1024 * 1024 && timeDiff > 300000) { // 50MB 增长且超过 5 分钟
            System.out.println("\n🚨 POTENTIAL MEMORY LEAK DETECTED!");
            System.out.println("Recommendations:");
            System.out.println("  1. Generate and analyze heap dumps");
            System.out.println("  2. Review static collections and caches");
            System.out.println("  3. Check for unclosed resources");
            System.out.println("  4. Verify listener cleanup");
        } else {
            System.out.println("\n✅ No obvious memory leaks detected");
        }
    }
    
    // 内存快照数据结构
    static class MemorySnapshot {
        final long timestamp;
        final long heapUsed;
        final long heapCommitted;
        final long heapMax;
        final long totalGcCount;
        final long totalGcTime;
        
        public MemorySnapshot(long timestamp, long heapUsed, long heapCommitted,
                            long heapMax, long totalGcCount, long totalGcTime) {
            this.timestamp = timestamp;
            this.heapUsed = heapUsed;
            this.heapCommitted = heapCommitted;
            this.heapMax = heapMax;
            this.totalGcCount = totalGcCount;
            this.totalGcTime = totalGcTime;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        MemoryLeakDetector detector = new MemoryLeakDetector();
        
        // 启动检测(每 10 秒检查一次)
        detector.startDetection(10);
        
        // 模拟应用程序运行
        System.out.println("Simulating application with potential memory leaks...");
        
        // 运行一些可能导致内存泄漏的代码
        MemoryLeakExamples examples = new MemoryLeakExamples();
        examples.demonstrateStaticCollectionLeak();
        
        // 运行 5 分钟
        Thread.sleep(300000);
        
        detector.stopDetection();
         detector.printDetectionSummary();
     }
 }

5. 内存优化策略

5.1 对象生命周期管理

5.1.1 对象池模式

// 对象池实现示例
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ObjectPoolExample {
    
    // 重对象示例
    static class ExpensiveObject {
        private final int id;
        private final byte[] data;
        private volatile boolean inUse;
        
        public ExpensiveObject(int id) {
            this.id = id;
            this.data = new byte[10240]; // 10KB
            this.inUse = false;
            
            // 模拟昂贵的初始化过程
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        public void reset() {
            // 重置对象状态以便重用
            java.util.Arrays.fill(data, (byte) 0);
            inUse = false;
        }
        
        public void use() {
            inUse = true;
            // 模拟对象使用
            java.util.Arrays.fill(data, (byte) id);
        }
        
        public boolean isInUse() {
            return inUse;
        }
        
        public int getId() {
            return id;
        }
    }
    
    // 简单对象池实现
    static class SimpleObjectPool {
        private final BlockingQueue<ExpensiveObject> pool;
        private final AtomicInteger createdCount;
        private final int maxSize;
        
        public SimpleObjectPool(int maxSize) {
            this.pool = new LinkedBlockingQueue<>(maxSize);
            this.createdCount = new AtomicInteger(0);
            this.maxSize = maxSize;
        }
        
        public ExpensiveObject acquire() throws InterruptedException {
            ExpensiveObject obj = pool.poll();
            
            if (obj == null) {
                // 池中没有可用对象,创建新对象
                if (createdCount.get() < maxSize) {
                    obj = new ExpensiveObject(createdCount.incrementAndGet());
                    System.out.println("Created new object: " + obj.getId());
                } else {
                    // 等待可用对象
                    obj = pool.take();
                    System.out.println("Reused object from pool: " + obj.getId());
                }
            } else {
                System.out.println("Got object from pool: " + obj.getId());
            }
            
            return obj;
        }
        
        public void release(ExpensiveObject obj) {
            if (obj != null) {
                obj.reset();
                pool.offer(obj);
                System.out.println("Returned object to pool: " + obj.getId());
            }
        }
        
        public int getPoolSize() {
            return pool.size();
        }
        
        public int getCreatedCount() {
            return createdCount.get();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Object Pool Example ===");
        
        SimpleObjectPool pool = new SimpleObjectPool(5);
        
        // 模拟多线程使用对象池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        for (int i = 0; i < 20; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    ExpensiveObject obj = pool.acquire();
                    
                    // 使用对象
                    obj.use();
                    Thread.sleep(100); // 模拟使用时间
                    
                    // 归还对象
                    pool.release(obj);
                    
                    System.out.printf("Task %d completed, pool size: %d%n", 
                        taskId, pool.getPoolSize());
                        
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(30, TimeUnit.SECONDS);
        
        System.out.printf("\nFinal stats - Created objects: %d, Pool size: %d%n",
            pool.getCreatedCount(), pool.getPoolSize());
    }
}

5.1.2 弱引用和软引用

// 引用类型示例
import java.lang.ref.*;
import java.util.*;
import java.util.concurrent.*;

public class ReferenceTypesExample {
    
    // 缓存数据类
    static class CacheData {
        private final String key;
        private final byte[] data;
        private final long timestamp;
        
        public CacheData(String key, int size) {
            this.key = key;
            this.data = new byte[size];
            this.timestamp = System.currentTimeMillis();
            Arrays.fill(data, (byte) key.hashCode());
        }
        
        public String getKey() { return key; }
        public byte[] getData() { return data; }
        public long getTimestamp() { return timestamp; }
        
        @Override
        public String toString() {
            return String.format("CacheData{key='%s', size=%d, age=%dms}",
                key, data.length, System.currentTimeMillis() - timestamp);
        }
    }
    
    // 使用软引用的缓存
    static class SoftReferenceCache {
        private final Map<String, SoftReference<CacheData>> cache;
        private final ReferenceQueue<CacheData> referenceQueue;
        
        public SoftReferenceCache() {
            this.cache = new ConcurrentHashMap<>();
            this.referenceQueue = new ReferenceQueue<>();
        }
        
        public void put(String key, CacheData data) {
            cleanupStaleReferences();
            cache.put(key, new SoftReference<>(data, referenceQueue));
            System.out.println("Cached: " + key);
        }
        
        public CacheData get(String key) {
            SoftReference<CacheData> ref = cache.get(key);
            if (ref != null) {
                CacheData data = ref.get();
                if (data != null) {
                    System.out.println("Cache hit: " + key);
                    return data;
                } else {
                    // 软引用已被回收
                    cache.remove(key);
                    System.out.println("Cache miss (GC'd): " + key);
                }
            } else {
                System.out.println("Cache miss: " + key);
            }
            return null;
        }
        
        private void cleanupStaleReferences() {
            Reference<? extends CacheData> ref;
            while ((ref = referenceQueue.poll()) != null) {
                // 清理已被回收的引用
                cache.values().removeIf(softRef -> softRef == ref);
            }
        }
        
        public int size() {
            cleanupStaleReferences();
            return cache.size();
        }
        
        public void printStats() {
            cleanupStaleReferences();
            System.out.printf("Cache size: %d entries%n", cache.size());
            
            long totalSize = cache.values().stream()
                .mapToLong(ref -> {
                    CacheData data = ref.get();
                    return data != null ? data.getData().length : 0;
                })
                .sum();
            
            System.out.printf("Total cached data: %.2f MB%n", totalSize / 1024.0 / 1024.0);
        }
    }
    
    // 使用弱引用的监听器管理
    static class WeakListenerManager {
        private final List<WeakReference<EventListener>> listeners;
        
        public WeakListenerManager() {
            this.listeners = new ArrayList<>();
        }
        
        public void addListener(EventListener listener) {
            listeners.add(new WeakReference<>(listener));
            System.out.println("Added listener: " + listener.getClass().getSimpleName());
        }
        
        public void fireEvent(String event) {
            Iterator<WeakReference<EventListener>> iterator = listeners.iterator();
            int activeListeners = 0;
            
            while (iterator.hasNext()) {
                WeakReference<EventListener> ref = iterator.next();
                EventListener listener = ref.get();
                
                if (listener != null) {
                    listener.onEvent(event);
                    activeListeners++;
                } else {
                    // 监听器已被回收,移除弱引用
                    iterator.remove();
                }
            }
            
            System.out.printf("Event '%s' fired to %d listeners%n", event, activeListeners);
        }
        
        public int getListenerCount() {
            // 清理已回收的引用
            listeners.removeIf(ref -> ref.get() == null);
            return listeners.size();
        }
    }
    
    interface EventListener {
        void onEvent(String event);
    }
    
    static class MyEventListener implements EventListener {
        private final String name;
        private final byte[] data;
        
        public MyEventListener(String name) {
            this.name = name;
            this.data = new byte[1024]; // 1KB
        }
        
        @Override
        public void onEvent(String event) {
            System.out.println(name + " received event: " + event);
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Reference Types Example ===");
        
        // 演示软引用缓存
        demonstrateSoftReferenceCache();
        
        System.out.println("\n" + "=".repeat(50));
        
        // 演示弱引用监听器
        demonstrateWeakReferenceListeners();
    }
    
    private static void demonstrateSoftReferenceCache() throws InterruptedException {
        System.out.println("\n=== Soft Reference Cache Demo ===");
        
        SoftReferenceCache cache = new SoftReferenceCache();
        
        // 添加一些缓存数据
        for (int i = 0; i < 100; i++) {
            String key = "data" + i;
            CacheData data = new CacheData(key, 1024 * 1024); // 1MB per item
            cache.put(key, data);
        }
        
        cache.printStats();
        
        // 访问一些缓存项
        System.out.println("\nAccessing cached items:");
        for (int i = 0; i < 10; i++) {
            cache.get("data" + i);
        }
        
        // 创建内存压力,触发软引用回收
        System.out.println("\nCreating memory pressure...");
        try {
            List<byte[]> memoryPressure = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                memoryPressure.add(new byte[1024 * 1024]); // 1MB
            }
        } catch (OutOfMemoryError e) {
            System.out.println("OutOfMemoryError caught (expected)");
        }
        
        // 强制 GC
        System.gc();
        Thread.sleep(1000);
        
        System.out.println("\nAfter GC:");
        cache.printStats();
        
        // 再次访问缓存项
        System.out.println("\nAccessing cached items after GC:");
        for (int i = 0; i < 10; i++) {
            cache.get("data" + i);
        }
    }
    
    private static void demonstrateWeakReferenceListeners() throws InterruptedException {
        System.out.println("\n=== Weak Reference Listeners Demo ===");
        
        WeakListenerManager manager = new WeakListenerManager();
        
        // 添加一些监听器
        for (int i = 0; i < 10; i++) {
            MyEventListener listener = new MyEventListener("Listener" + i);
            manager.addListener(listener);
            
            // 只保持前 5 个监听器的强引用
            if (i >= 5) {
                // 让后 5 个监听器可以被 GC
                listener = null;
            }
        }
        
        System.out.printf("\nInitial listener count: %d%n", manager.getListenerCount());
        
        // 触发事件
        manager.fireEvent("Initial Event");
        
        // 强制 GC
        System.out.println("\nForcing garbage collection...");
        System.gc();
        Thread.sleep(1000);
        
        System.out.printf("Listener count after GC: %d%n", manager.getListenerCount());
        
        // 再次触发事件
        manager.fireEvent("Post-GC Event");
    }
}

5.2 内存使用优化

5.2.1 字符串优化

// 字符串内存优化示例
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class StringOptimizationExample {
    
    // 字符串池实现
    static class StringPool {
        private final Map<String, String> pool = new ConcurrentHashMap<>();
        private long memoryBefore;
        private long memoryAfter;
        
        public String intern(String str) {
            if (str == null) return null;
            
            return pool.computeIfAbsent(str, k -> k);
        }
        
        public int size() {
            return pool.size();
        }
        
        public void clear() {
            pool.clear();
        }
        
        public void measureMemoryBefore() {
            System.gc();
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            memoryBefore = getUsedMemory();
        }
        
        public void measureMemoryAfter() {
            System.gc();
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            memoryAfter = getUsedMemory();
        }
        
        public void printMemoryStats() {
            long memoryDiff = memoryAfter - memoryBefore;
            System.out.printf("Memory usage: %.2f MB -> %.2f MB (diff: %+.2f MB)%n",
                memoryBefore / 1024.0 / 1024.0,
                memoryAfter / 1024.0 / 1024.0,
                memoryDiff / 1024.0 / 1024.0);
            System.out.printf("String pool size: %d unique strings%n", size());
        }
        
        private long getUsedMemory() {
            Runtime runtime = Runtime.getRuntime();
            return runtime.totalMemory() - runtime.freeMemory();
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== String Optimization Example ===");
        
        // 演示字符串重复问题
        demonstrateStringDuplication();
        
        System.out.println("\n" + "=".repeat(50));
        
        // 演示字符串池优化
        demonstrateStringPoolOptimization();
        
        System.out.println("\n" + "=".repeat(50));
        
        // 演示 StringBuilder 优化
        demonstrateStringBuilderOptimization();
    }
    
    private static void demonstrateStringDuplication() {
        System.out.println("\n=== String Duplication Problem ===");
        
        StringPool pool = new StringPool();
        pool.measureMemoryBefore();
        
        // 创建大量重复字符串(不使用池)
        List<String> duplicateStrings = new ArrayList<>();
        String[] patterns = {"user", "admin", "guest", "manager", "developer"};
        
        for (int i = 0; i < 10000; i++) {
            for (String pattern : patterns) {
                // 每次都创建新的字符串对象
                String str = new String(pattern + "_" + (i % 100));
                duplicateStrings.add(str);
            }
        }
        
        pool.measureMemoryAfter();
        
        System.out.printf("Created %d strings with duplication%n", duplicateStrings.size());
        pool.printMemoryStats();
    }
    
    private static void demonstrateStringPoolOptimization() {
        System.out.println("\n=== String Pool Optimization ===");
        
        StringPool pool = new StringPool();
        pool.measureMemoryBefore();
        
        // 使用字符串池减少重复
        List<String> pooledStrings = new ArrayList<>();
        String[] patterns = {"user", "admin", "guest", "manager", "developer"};
        
        for (int i = 0; i < 10000; i++) {
            for (String pattern : patterns) {
                String str = pool.intern(pattern + "_" + (i % 100));
                pooledStrings.add(str);
            }
        }
        
        pool.measureMemoryAfter();
        
        System.out.printf("Created %d strings with pooling%n", pooledStrings.size());
        pool.printMemoryStats();
    }
    
    private static void demonstrateStringBuilderOptimization() {
        System.out.println("\n=== StringBuilder Optimization ===");
        
        // 错误的字符串拼接方式
        long startTime = System.currentTimeMillis();
        String result1 = "";
        for (int i = 0; i < 1000; i++) {
            result1 += "Item " + i + ", ";
        }
        long time1 = System.currentTimeMillis() - startTime;
        
        // 正确的字符串拼接方式
        startTime = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder(50000); // 预分配容量
        for (int i = 0; i < 1000; i++) {
            sb.append("Item ").append(i).append(", ");
        }
        String result2 = sb.toString();
        long time2 = System.currentTimeMillis() - startTime;
        
        System.out.printf("String concatenation: %d ms%n", time1);
        System.out.printf("StringBuilder: %d ms%n", time2);
        System.out.printf("Performance improvement: %.1fx faster%n", (double) time1 / time2);
        
        // 验证结果相同
         System.out.printf("Results equal: %b%n", result1.equals(result2));
         System.out.printf("Result length: %d characters%n", result2.length());
     }
 }

6. 实践练习

练习 1:内存使用监控

目标:使用 VisualVM 监控应用程序的内存使用模式

步骤

  1. 创建测试应用
// MemoryMonitoringExample.java
import java.util.*;
import java.util.concurrent.*;

public class MemoryMonitoringExample {
    private static final List<byte[]> memoryConsumer = new ArrayList<>();
    private static final Random random = new Random();
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Memory Monitoring Example Started");
        System.out.println("PID: " + ProcessHandle.current().pid());
        
        // 阶段 1:稳定内存使用
        System.out.println("Phase 1: Stable memory usage");
        for (int i = 0; i < 100; i++) {
            memoryConsumer.add(new byte[1024 * 100]); // 100KB
            Thread.sleep(100);
        }
        
        // 阶段 2:内存增长
        System.out.println("Phase 2: Memory growth");
        for (int i = 0; i < 200; i++) {
            memoryConsumer.add(new byte[1024 * 500]); // 500KB
            Thread.sleep(50);
        }
        
        // 阶段 3:内存释放
        System.out.println("Phase 3: Memory release");
        for (int i = 0; i < 150; i++) {
            if (!memoryConsumer.isEmpty()) {
                memoryConsumer.remove(random.nextInt(memoryConsumer.size()));
            }
            Thread.sleep(30);
        }
        
        // 阶段 4:GC 压力测试
        System.out.println("Phase 4: GC pressure test");
        for (int i = 0; i < 1000; i++) {
            // 创建短生命周期对象
            byte[] temp = new byte[1024 * 1024]; // 1MB
            Arrays.fill(temp, (byte) i);
            Thread.sleep(10);
        }
        
        System.out.println("Example completed. Press Ctrl+C to exit.");
        Thread.sleep(Long.MAX_VALUE);
    }
}
  1. 监控步骤

    • 编译并运行测试应用
    • 在 VisualVM 中连接到应用程序
    • 观察 Monitor 标签页中的内存使用图表
    • 记录各个阶段的内存使用模式
    • 观察 GC 活动和频率
  2. 分析要点

    • 堆内存的增长和释放模式
    • GC 的触发时机和效果
    • 不同代的内存使用情况
    • 内存使用的峰值和平均值

练习 2:堆转储分析

目标:生成和分析堆转储,识别内存使用热点

步骤

  1. 创建内存泄漏示例
// HeapDumpExample.java
import java.util.*;
import java.util.concurrent.*;

public class HeapDumpExample {
    private static final Map<String, List<byte[]>> cache = new ConcurrentHashMap<>();
    private static final ScheduledExecutorService scheduler = 
        Executors.newScheduledThreadPool(2);
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Heap Dump Example Started");
        System.out.println("PID: " + ProcessHandle.current().pid());
        
        // 启动数据生产者
        scheduler.scheduleAtFixedRate(() -> {
            String key = "data_" + System.currentTimeMillis();
            List<byte[]> data = new ArrayList<>();
            
            // 每次添加 10MB 数据
            for (int i = 0; i < 10; i++) {
                data.add(new byte[1024 * 1024]); // 1MB
            }
            
            cache.put(key, data);
            System.out.printf("Added %s, cache size: %d%n", key, cache.size());
            
        }, 0, 2, TimeUnit.SECONDS);
        
        // 启动数据清理者(但清理不及时,造成内存泄漏)
        scheduler.scheduleAtFixedRate(() -> {
            if (cache.size() > 3) {
                String oldestKey = cache.keySet().iterator().next();
                cache.remove(oldestKey);
                System.out.printf("Removed %s, cache size: %d%n", oldestKey, cache.size());
            }
        }, 10, 10, TimeUnit.SECONDS);
        
        // 保持程序运行
        Thread.sleep(Long.MAX_VALUE);
    }
}
  1. 堆转储分析步骤

    • 运行示例程序 5-10 分钟
    • 在 VisualVM 中生成堆转储
    • 分析 Classes 视图,找出占用内存最多的类
    • 分析 Instances 视图,查看具体对象实例
    • 使用 OQL 查询特定对象
    • 分析对象的引用关系
  2. 分析要点

    • 识别内存占用最大的对象类型
    • 分析对象的引用链
    • 找出可能的内存泄漏点
    • 评估内存使用的合理性

练习 3:内存优化实践

目标:应用内存优化技术,对比优化前后的效果

步骤

  1. 创建优化前的代码
// BeforeOptimization.java
import java.util.*;

public class BeforeOptimization {
    private static final List<String> stringList = new ArrayList<>();
    private static final List<UserData> userList = new ArrayList<>();
    
    static class UserData {
        private String name;
        private String email;
        private String department;
        private List<String> permissions;
        
        public UserData(String name, String email, String department) {
            this.name = new String(name); // 不必要的字符串复制
            this.email = new String(email);
            this.department = new String(department);
            this.permissions = new ArrayList<>();
        }
        
        public void addPermission(String permission) {
            permissions.add(new String(permission)); // 不必要的字符串复制
        }
        
        public String getFullInfo() {
            String info = "";
            info += "Name: " + name + ", ";
            info += "Email: " + email + ", ";
            info += "Department: " + department + ", ";
            info += "Permissions: " + permissions.toString();
            return info;
        }
    }
    
    public static void main(String[] args) {
        System.out.println("Before Optimization Example");
        
        // 创建大量重复字符串
        String[] departments = {"Engineering", "Marketing", "Sales", "HR", "Finance"};
        String[] permissions = {"READ", "write", "admin", "user", "guest"};
        
        for (int i = 0; i < 10000; i++) {
            String name = "User" + i;
            String email = "user" + i + "@company.com";
            String dept = departments[i % departments.length];
            
            UserData user = new UserData(name, email, dept);
            
            // 添加权限
            for (int j = 0; j < 3; j++) {
                user.addPermission(permissions[j % permissions.length]);
            }
            
            userList.add(user);
            stringList.add(user.getFullInfo());
        }
        
        System.out.printf("Created %d users and %d info strings%n", 
            userList.size(), stringList.size());
        
        // 保持程序运行以便分析
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
  1. 创建优化后的代码
// AfterOptimization.java
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AfterOptimization {
    private static final List<String> stringList = new ArrayList<>();
    private static final List<UserData> userList = new ArrayList<>();
    private static final StringPool stringPool = new StringPool();
    
    // 字符串池
    static class StringPool {
        private final Map<String, String> pool = new ConcurrentHashMap<>();
        
        public String intern(String str) {
            return pool.computeIfAbsent(str, k -> k);
        }
    }
    
    static class UserData {
        private final String name;
        private final String email;
        private final String department;
        private final List<String> permissions;
        
        public UserData(String name, String email, String department) {
            this.name = stringPool.intern(name);
            this.email = stringPool.intern(email);
            this.department = stringPool.intern(department);
            this.permissions = new ArrayList<>();
        }
        
        public void addPermission(String permission) {
            permissions.add(stringPool.intern(permission));
        }
        
        public String getFullInfo() {
            StringBuilder sb = new StringBuilder(200); // 预分配容量
            sb.append("Name: ").append(name).append(", ");
            sb.append("Email: ").append(email).append(", ");
            sb.append("Department: ").append(department).append(", ");
            sb.append("Permissions: ").append(permissions.toString());
            return sb.toString();
        }
    }
    
    public static void main(String[] args) {
        System.out.println("After Optimization Example");
        
        // 预先将常用字符串加入池中
        String[] departments = {"Engineering", "Marketing", "Sales", "HR", "Finance"};
        String[] permissions = {"read", "write", "admin", "user", "guest"};
        
        for (String dept : departments) {
            stringPool.intern(dept);
        }
        for (String perm : permissions) {
            stringPool.intern(perm);
        }
        
        for (int i = 0; i < 10000; i++) {
            String name = "User" + i;
            String email = "user" + i + "@company.com";
            String dept = departments[i % departments.length];
            
            UserData user = new UserData(name, email, dept);
            
            // 添加权限
            for (int j = 0; j < 3; j++) {
                user.addPermission(permissions[j % permissions.length]);
            }
            
            userList.add(user);
            stringList.add(user.getFullInfo());
        }
        
        System.out.printf("Created %d users and %d info strings%n", 
            userList.size(), stringList.size());
        
        // 保持程序运行以便分析
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
  1. 对比分析步骤

    • 分别运行优化前后的代码
    • 使用 VisualVM 监控内存使用
    • 生成堆转储进行对比
    • 分析字符串对象的数量和内存占用
    • 记录 GC 活动的差异
  2. 分析要点

    • 内存使用量的减少
    • 对象数量的变化
    • GC 频率和耗时的改善
    • 字符串去重的效果

7. 本章总结

7.1 关键要点

  1. Java 内存模型理解

    • 掌握 JVM 内存结构(堆、栈、方法区等)
    • 理解垃圾回收机制和分代收集
    • 了解不同内存区域的作用和特点
  2. VisualVM 内存监控

    • 熟练使用 Monitor 标签页监控内存使用
    • 理解内存使用图表和 GC 活动指标
    • 掌握实时内存监控的技巧
  3. 堆转储分析

    • 掌握堆转储的生成方法(手动、程序化、命令行)
    • 熟练使用 VisualVM 分析堆转储
    • 理解对象引用关系和内存占用分布
  4. 内存泄漏检测

    • 识别常见的内存泄漏模式
    • 使用 VisualVM 检测内存泄漏
    • 掌握内存泄漏的分析和定位技巧
  5. 内存优化策略

    • 对象生命周期管理(对象池、引用类型)
    • 字符串优化(字符串池、StringBuilder)
    • 集合类优化和容量预分配

7.2 最佳实践

  1. 监控策略

    • 建立内存监控基线
    • 定期进行内存使用分析
    • 设置合理的内存告警阈值
    • 结合业务场景分析内存使用模式
  2. 内存泄漏预防

    • 及时释放不再使用的对象引用
    • 正确使用集合类,避免无限增长
    • 合理使用缓存,设置过期策略
    • 注意监听器和回调的生命周期管理
  3. 性能优化

    • 选择合适的数据结构和算法
    • 合理使用对象池和缓存
    • 优化字符串操作和拼接
    • 预分配集合容量,减少扩容开销
  4. 故障诊断

    • 建立内存问题的诊断流程
    • 保存关键时刻的堆转储
    • 结合日志和监控数据分析
    • 制定内存问题的应急处理方案

7.3 进阶学习

完成本章学习后,建议继续深入以下主题:

  1. 高级内存分析

    • JVM 内存参数调优
    • 不同垃圾收集器的特点和选择
    • 内存映射文件和直接内存
    • 大对象和长生命周期对象的处理
  2. 生产环境实践

    • 生产环境内存监控方案
    • 内存问题的自动化检测
    • 内存使用的容量规划
    • 内存相关的性能基准测试
  3. 工具集成

    • 与 APM 工具的集成
    • 自动化内存分析脚本
    • CI/CD 中的内存测试
    • 内存分析报告的自动生成

下一章我们将学习 CPU 性能分析与采样,探讨如何使用 VisualVM 分析应用程序的 CPU 使用情况,识别性能瓶颈,并进行针对性的优化。 “`