当前位置: 首页 > news >正文

第4章:内存分析与堆转储

本章概述

内存分析是 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);// 手动触发 GCSystem.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]; // 1KBArrays.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]; // 1MBArrays.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]; // 512KBArrays.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 objectif (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() {@Overridepublic 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);// 触发 GCSystem.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));}@Overridepublic 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 的 APIpublic 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 MBresult.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");@Overridepublic 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]; // 10KBArrays.fill(data, (byte) name.hashCode());}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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 secondif (growthRate > 1024 * 1024) { // > 1MB/sSystem.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]; // 10KBthis.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; }@Overridepublic 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}@Overridepublic 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 itemcache.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)");}// 强制 GCSystem.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 个监听器可以被 GClistener = null;}}System.out.printf("\nInitial listener count: %d%n", manager.getListenerCount());// 触发事件manager.fireEvent("Initial Event");// 强制 GCSystem.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]); // 100KBThread.sleep(100);}// 阶段 2:内存增长System.out.println("Phase 2: Memory growth");for (int i = 0; i < 200; i++) {memoryConsumer.add(new byte[1024 * 500]); // 500KBThread.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]; // 1MBArrays.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 使用情况,识别性能瓶颈,并进行针对性的优化。

http://www.xdnf.cn/news/1435555.html

相关文章:

  • 命令行文本处理小工具:cut、sort、uniq、tr 详解与应用
  • EMQX 4.4 加mysql认证
  • BandiZip下载与详细图文安装教程!!
  • docker 安装 redis 并设置 volumes 并修改 修改密码(二)
  • 构建可扩展的 AI 应用:LangChain 与 MCP 服务的集成模式
  • C++算法学习:位运算
  • ECMWF数据批量下载(Windows版本)
  • Ngene:实验设计的尖端利器
  • 洛谷P3811 【模板】模意义下的乘法逆元
  • Linux操作系统(6)
  • java-设计模式-3-创建型模式-原型
  • 一文读懂 Python 【循环语句】:从基础到实战,效率提升指南
  • 【机器学习学习笔记】Matplotlib 基本操作
  • Java 大视界 --Java 大数据在智能教育学习资源整合与知识图谱构建中的深度应用(406)
  • 如何将大疆无人机拍摄到的图像回传到应急指挥中心大屏?5G单兵图传轻松解决图传问题|伟博视讯
  • Ansible角色:高效开发与管理的秘密
  • Ukey介绍
  • HTML第二课:块级元素
  • 【3D 入门-3】常见 3D 格式对比,.glb / .obj / .stl / .ply
  • Ascend上开发自定义算子接入PyTorch有几种实现方式?
  • Higress云原生API网关详解 与 Linux版本安装指南
  • 企业数字安全守护神:IT运维管理系统全面解析,构建坚不可摧的防护体系
  • 实现自己的AI视频监控系统-第三章-信息的推送与共享3(重点)
  • 数据结构:闭散列 (Closed Hashing)-开放定址法 (Open Addressing)
  • react用useImages读取图片,方便backgroundImage
  • hikvision海康威视sdk调用失败,code为29解决办法
  • 集采与反腐双重压力下,医药销售的破局之道:从资源依赖到价值重构
  • 从结构化到多模态:RAG文档解析工具选型全指南
  • Portainer:Docker可视化管理神器部署与使用攻略
  • 不只是一台玩具车:开源燃料电池机器人HydroBot全揭秘