第6章:垃圾回收分析与调优

1. 垃圾回收基础

1.1 Java 垃圾回收概述

垃圾回收(Garbage Collection,GC)是 Java 虚拟机自动内存管理的核心机制。理解 GC 的工作原理对于 Java 应用性能调优至关重要。

1.1.1 垃圾回收的目标
  • 自动内存管理:无需手动释放内存
  • 防止内存泄漏:回收不再使用的对象
  • 优化内存使用:整理内存碎片
  • 保证应用稳定性:避免内存溢出
1.1.2 垃圾回收的挑战
  • 停顿时间:GC 过程中应用暂停
  • 吞吐量影响:GC 消耗 CPU 资源
  • 内存开销:GC 算法本身需要内存
  • 调优复杂性:参数众多,相互影响

1.2 垃圾回收算法

1.2.1 标记-清除算法(Mark-Sweep)
java 复制代码
// 标记-清除算法演示
import java.util.*;
import java.lang.ref.*;

public class MarkSweepDemo {
    
    private static final List<Object> roots = new ArrayList<>();
    private static final Set<Object> markedObjects = new HashSet<>();
    
    public static void main(String[] args) {
        System.out.println("=== Mark-Sweep Algorithm Demo ===");
        
        // 创建对象图
        createObjectGraph();
        
        // 模拟标记阶段
        markPhase();
        
        // 模拟清除阶段
        sweepPhase();
        
        System.out.println("Mark-Sweep completed");
    }
    
    private static void createObjectGraph() {
        // 创建根对象
        Node root1 = new Node("Root1");
        Node root2 = new Node("Root2");
        
        // 创建可达对象
        Node child1 = new Node("Child1");
        Node child2 = new Node("Child2");
        Node grandChild = new Node("GrandChild");
        
        // 建立引用关系
        root1.addChild(child1);
        root1.addChild(child2);
        child1.addChild(grandChild);
        
        // 创建不可达对象
        Node orphan1 = new Node("Orphan1");
        Node orphan2 = new Node("Orphan2");
        orphan1.addChild(orphan2);
        
        // 添加到根集合
        roots.add(root1);
        roots.add(root2);
        
        System.out.println("Object graph created");
    }
    
    private static void markPhase() {
        System.out.println("\n=== Mark Phase ===");
        
        // 从根对象开始标记
        for (Object root : roots) {
            markReachable(root);
        }
        
        System.out.println("Marked " + markedObjects.size() + " objects");
    }
    
    private static void markReachable(Object obj) {
        if (obj == null || markedObjects.contains(obj)) {
            return;
        }
        
        // 标记当前对象
        markedObjects.add(obj);
        System.out.println("Marked: " + obj);
        
        // 递归标记子对象
        if (obj instanceof Node) {
            Node node = (Node) obj;
            for (Node child : node.getChildren()) {
                markReachable(child);
            }
        }
    }
    
    private static void sweepPhase() {
        System.out.println("\n=== Sweep Phase ===");
        
        // 在实际 JVM 中,这里会遍历整个堆
        // 清除未标记的对象
        System.out.println("Sweeping unmarked objects...");
        
        // 清理标记
        markedObjects.clear();
        System.out.println("Sweep phase completed");
    }
    
    static class Node {
        private final String name;
        private final List<Node> children = new ArrayList<>();
        
        public Node(String name) {
            this.name = name;
        }
        
        public void addChild(Node child) {
            children.add(child);
        }
        
        public List<Node> getChildren() {
            return children;
        }
        
        @Override
        public String toString() {
            return "Node{" + name + "}";
        }
    }
}

垃圾回收器选择与调优

常见垃圾回收器对比

垃圾回收器 适用场景 优点 缺点
Serial GC 单核CPU,小内存应用 简单,内存占用少 停顿时间长
Parallel GC 多核CPU,吞吐量优先 高吞吐量 停顿时间较长
CMS GC 低延迟要求 并发收集,停顿时间短 内存碎片,CPU占用高
G1 GC 大内存,低延迟 可预测停顿时间 复杂度高
ZGC/Shenandoah 超大内存,极低延迟 极短停顿时间 吞吐量较低

GC 参数调优示例

java 复制代码
// GCTuningExample.java
public class GCTuningExample {
    private static final int OBJECT_COUNT = 1000000;
    private static final int ITERATIONS = 10;
    
    public static void main(String[] args) {
        System.out.println("GC调优示例程序启动");
        System.out.println("JVM参数: " + 
            java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments());
        
        // 预热JVM
        warmUp();
        
        // 执行测试
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            performMemoryIntensiveTask(i);
            System.gc(); // 建议进行垃圾回收
        }
        long endTime = System.currentTimeMillis();
        
        System.out.println("总执行时间: " + (endTime - startTime) + "ms");
        printMemoryInfo();
    }
    
    private static void warmUp() {
        System.out.println("JVM预热中...");
        for (int i = 0; i < 3; i++) {
            createObjects(OBJECT_COUNT / 10);
        }
        System.gc();
    }
    
    private static void performMemoryIntensiveTask(int iteration) {
        System.out.println("执行第 " + (iteration + 1) + " 次内存密集任务");
        
        // 创建大量对象
        List<String> objects = createObjects(OBJECT_COUNT);
        
        // 模拟对象使用
        processObjects(objects);
        
        // 清理引用
        objects.clear();
        objects = null;
    }
    
    private static List<String> createObjects(int count) {
        List<String> objects = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            objects.add("Object_" + i + "_" + System.nanoTime());
        }
        return objects;
    }
    
    private static void processObjects(List<String> objects) {
        // 模拟对象处理
        int sum = 0;
        for (String obj : objects) {
            sum += obj.hashCode();
        }
        // 防止编译器优化
        if (sum == Integer.MAX_VALUE) {
            System.out.println("Unlikely case");
        }
    }
    
    private static void printMemoryInfo() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        
        System.out.println("\n=== 内存使用情况 ===");
        System.out.println("总内存: " + formatBytes(totalMemory));
        System.out.println("已用内存: " + formatBytes(usedMemory));
        System.out.println("空闲内存: " + formatBytes(freeMemory));
        System.out.println("最大内存: " + formatBytes(runtime.maxMemory()));
    }
    
    private static String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
        return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
    }
}

不同GC的JVM参数配置

1. Parallel GC(默认)
bash 复制代码
# 基本配置
java -XX:+UseParallelGC \
     -XX:ParallelGCThreads=4 \
     -XX:MaxGCPauseMillis=200 \
     -Xms2g -Xmx4g \
     GCTuningExample

# 优化配置
java -XX:+UseParallelGC \
     -XX:+UseParallelOldGC \
     -XX:ParallelGCThreads=8 \
     -XX:GCTimeRatio=19 \
     -XX:MaxGCPauseMillis=100 \
     -Xms4g -Xmx8g \
     GCTuningExample
2. G1 GC
bash 复制代码
# 基本配置
java -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=100 \
     -XX:G1HeapRegionSize=16m \
     -Xms2g -Xmx4g \
     GCTuningExample

# 优化配置
java -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=50 \
     -XX:G1HeapRegionSize=32m \
     -XX:G1NewSizePercent=20 \
     -XX:G1MaxNewSizePercent=40 \
     -XX:G1MixedGCCountTarget=8 \
     -XX:G1MixedGCLiveThresholdPercent=85 \
     -Xms4g -Xmx8g \
     GCTuningExample
3. ZGC(Java 11+)
bash 复制代码
# 基本配置
java -XX:+UseZGC \
     -XX:+UnlockExperimentalVMOptions \
     -Xms2g -Xmx4g \
     GCTuningExample

# 优化配置
java -XX:+UseZGC \
     -XX:+UnlockExperimentalVMOptions \
     -XX:ZCollectionInterval=5 \
     -XX:ZUncommitDelay=300 \
     -Xms8g -Xmx16g \
     GCTuningExample

GC 性能调优策略

调优步骤

  1. 建立基线

    • 记录当前GC性能指标
    • 确定性能目标(吞吐量 vs 延迟)
    • 分析应用特征
  2. 选择合适的GC

    • 根据应用需求选择GC算法
    • 考虑内存大小和延迟要求
    • 评估CPU资源
  3. 参数调优

    • 堆大小调优
    • 分代比例调优
    • GC线程数调优
  4. 监控和验证

    • 使用VisualVM监控GC性能
    • 分析GC日志
    • 验证性能改进

GC调优工具类

java 复制代码
// GCTuningHelper.java
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.*;

public class GCTuningHelper {
    private static final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    private static final List<GarbageCollectorMXBean> gcBeans = 
        ManagementFactory.getGarbageCollectorMXBeans();
    
    private final Map<String, Long> lastGCCounts = new HashMap<>();
    private final Map<String, Long> lastGCTimes = new HashMap<>();
    
    public void startMonitoring() {
        // 初始化基线数据
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            lastGCCounts.put(gcBean.getName(), gcBean.getCollectionCount());
            lastGCTimes.put(gcBean.getName(), gcBean.getCollectionTime());
        }
        
        System.out.println("GC监控已启动");
        printCurrentGCInfo();
    }
    
    public GCMetrics getGCMetrics() {
        GCMetrics metrics = new GCMetrics();
        
        // 内存使用情况
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        metrics.heapUsed = heapUsage.getUsed();
        metrics.heapMax = heapUsage.getMax();
        metrics.heapUtilization = (double) heapUsage.getUsed() / heapUsage.getMax() * 100;
        
        // GC统计信息
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            String gcName = gcBean.getName();
            long currentCount = gcBean.getCollectionCount();
            long currentTime = gcBean.getCollectionTime();
            
            long lastCount = lastGCCounts.getOrDefault(gcName, 0L);
            long lastTime = lastGCTimes.getOrDefault(gcName, 0L);
            
            GCInfo gcInfo = new GCInfo();
            gcInfo.name = gcName;
            gcInfo.totalCollections = currentCount;
            gcInfo.totalTime = currentTime;
            gcInfo.recentCollections = currentCount - lastCount;
            gcInfo.recentTime = currentTime - lastTime;
            
            if (gcInfo.recentCollections > 0) {
                gcInfo.averageTime = (double) gcInfo.recentTime / gcInfo.recentCollections;
            }
            
            metrics.gcInfos.add(gcInfo);
            
            // 更新基线
            lastGCCounts.put(gcName, currentCount);
            lastGCTimes.put(gcName, currentTime);
        }
        
        return metrics;
    }
    
    public void printGCReport() {
        GCMetrics metrics = getGCMetrics();
        
        System.out.println("\n=== GC性能报告 ===");
        System.out.printf("堆内存使用: %s / %s (%.2f%%)\n",
            formatBytes(metrics.heapUsed),
            formatBytes(metrics.heapMax),
            metrics.heapUtilization);
        
        System.out.println("\nGC统计信息:");
        for (GCInfo gcInfo : metrics.gcInfos) {
            System.out.printf("  %s:\n", gcInfo.name);
            System.out.printf("    总收集次数: %d\n", gcInfo.totalCollections);
            System.out.printf("    总收集时间: %dms\n", gcInfo.totalTime);
            System.out.printf("    最近收集次数: %d\n", gcInfo.recentCollections);
            System.out.printf("    最近收集时间: %dms\n", gcInfo.recentTime);
            if (gcInfo.averageTime > 0) {
                System.out.printf("    平均收集时间: %.2fms\n", gcInfo.averageTime);
            }
        }
    }
    
    public void printCurrentGCInfo() {
        System.out.println("\n=== 当前GC配置 ===");
        
        // 打印GC算法
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            System.out.println("GC算法: " + gcBean.getName());
        }
        
        // 打印内存池信息
        List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
        System.out.println("\n内存池信息:");
        for (MemoryPoolMXBean pool : memoryPools) {
            MemoryUsage usage = pool.getUsage();
            if (usage != null) {
                System.out.printf("  %s: %s / %s\n",
                    pool.getName(),
                    formatBytes(usage.getUsed()),
                    formatBytes(usage.getMax()));
            }
        }
    }
    
    private String formatBytes(long bytes) {
        if (bytes < 0) return "N/A";
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
        return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
    }
    
    // 内部类
    public static class GCMetrics {
        public long heapUsed;
        public long heapMax;
        public double heapUtilization;
        public List<GCInfo> gcInfos = new ArrayList<>();
    }
    
    public static class GCInfo {
        public String name;
        public long totalCollections;
        public long totalTime;
        public long recentCollections;
        public long recentTime;
        public double averageTime;
    }
}
复制代码
## 实践练习

### 练习1:GC监控与分析

创建一个程序来观察不同GC算法的性能表现:

```java
// GCMonitoringExercise.java
import java.util.*;
import java.util.concurrent.*;

public class GCMonitoringExercise {
    private static final int THREAD_COUNT = 4;
    private static final int OBJECTS_PER_THREAD = 500000;
    private static final int ITERATIONS = 5;
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== GC监控练习 ===");
        
        GCTuningHelper gcHelper = new GCTuningHelper();
        gcHelper.startMonitoring();
        
        // 执行多轮测试
        for (int round = 1; round <= ITERATIONS; round++) {
            System.out.println("\n--- 第 " + round + " 轮测试 ---");
            
            // 多线程创建对象
            ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
            CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
            
            for (int i = 0; i < THREAD_COUNT; i++) {
                final int threadId = i;
                executor.submit(() -> {
                    try {
                        createObjectsInThread(threadId, OBJECTS_PER_THREAD);
                    } finally {
                        latch.countDown();
                    }
                });
            }
            
            latch.await();
            executor.shutdown();
            
            // 强制GC并等待
            System.gc();
            Thread.sleep(1000);
            
            // 打印GC报告
            gcHelper.printGCReport();
        }
        
        System.out.println("\n=== 练习完成 ===");
    }
    
    private static void createObjectsInThread(int threadId, int objectCount) {
        List<String> objects = new ArrayList<>();
        Random random = new Random();
        
        for (int i = 0; i < objectCount; i++) {
            // 创建不同大小的字符串
            int size = random.nextInt(100) + 10;
            StringBuilder sb = new StringBuilder(size);
            for (int j = 0; j < size; j++) {
                sb.append((char) ('a' + random.nextInt(26)));
            }
            objects.add(sb.toString());
            
            // 偶尔清理一些对象
            if (i % 10000 == 0 && !objects.isEmpty()) {
                objects.subList(0, Math.min(1000, objects.size())).clear();
            }
        }
        
        System.out.println("线程 " + threadId + " 完成对象创建");
    }
}

练习步骤:

  1. 使用不同的GC参数运行程序
  2. 在VisualVM中观察GC行为
  3. 比较不同GC算法的性能差异
  4. 分析GC日志和监控数据

练习2:内存泄漏与GC压力测试

java 复制代码
// GCPressureTest.java
import java.util.*;
import java.util.concurrent.*;

public class GCPressureTest {
    private static final Map<String, Object> memoryLeak = new ConcurrentHashMap<>();
    private static final List<byte[]> largeObjects = Collections.synchronizedList(new ArrayList<>());
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== GC压力测试 ===");
        
        GCTuningHelper gcHelper = new GCTuningHelper();
        gcHelper.startMonitoring();
        
        // 启动内存泄漏模拟
        Thread leakThread = new Thread(() -> simulateMemoryLeak());
        leakThread.setDaemon(true);
        leakThread.start();
        
        // 启动大对象创建
        Thread largeObjectThread = new Thread(() -> createLargeObjects());
        largeObjectThread.setDaemon(true);
        largeObjectThread.start();
        
        // 定期报告GC状态
        for (int i = 0; i < 10; i++) {
            Thread.sleep(5000);
            System.out.println("\n--- " + (i + 1) + " 分钟后 ---");
            gcHelper.printGCReport();
            
            // 检查内存使用情况
            Runtime runtime = Runtime.getRuntime();
            long usedMemory = runtime.totalMemory() - runtime.freeMemory();
            long maxMemory = runtime.maxMemory();
            double memoryUsage = (double) usedMemory / maxMemory * 100;
            
            System.out.printf("内存使用率: %.2f%%\n", memoryUsage);
            
            if (memoryUsage > 80) {
                System.out.println("警告:内存使用率过高!");
            }
        }
        
        System.out.println("\n=== 测试完成 ===");
    }
    
    private static void simulateMemoryLeak() {
        int counter = 0;
        while (true) {
            try {
                // 模拟内存泄漏
                String key = "leak_" + counter++;
                memoryLeak.put(key, new byte[1024]); // 1KB对象
                
                // 偶尔清理一些,但不是全部
                if (counter % 1000 == 0) {
                    Iterator<String> iterator = memoryLeak.keySet().iterator();
                    for (int i = 0; i < 100 && iterator.hasNext(); i++) {
                        iterator.next();
                        iterator.remove();
                    }
                }
                
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
    }
    
    private static void createLargeObjects() {
        Random random = new Random();
        while (true) {
            try {
                // 创建大对象
                int size = random.nextInt(1024 * 1024) + 512 * 1024; // 0.5-1.5MB
                largeObjects.add(new byte[size]);
                
                // 保持一定数量的大对象
                if (largeObjects.size() > 10) {
                    largeObjects.remove(0);
                }
                
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("内存不足,清理大对象");
                largeObjects.clear();
            }
        }
    }
}

练习3:GC调优对比

创建一个脚本来测试不同GC配置的性能:

bash 复制代码
#!/bin/bash
# gc_comparison.sh

echo "=== GC性能对比测试 ==="

# 测试程序
TEST_CLASS="GCMonitoringExercise"
JAR_FILE="gc-test.jar"

# 不同的GC配置
declare -a GC_CONFIGS=(
    "-XX:+UseSerialGC -Xms1g -Xmx2g"
    "-XX:+UseParallelGC -Xms1g -Xmx2g"
    "-XX:+UseG1GC -Xms1g -Xmx2g -XX:MaxGCPauseMillis=100"
    "-XX:+UseZGC -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx2g"
)

declare -a GC_NAMES=(
    "Serial GC"
    "Parallel GC"
    "G1 GC"
    "ZGC"
)

# 结果文件
RESULT_FILE="gc_comparison_results.txt"
echo "GC性能对比测试结果" > $RESULT_FILE
echo "测试时间: $(date)" >> $RESULT_FILE
echo "" >> $RESULT_FILE

# 运行测试
for i in "${!GC_CONFIGS[@]}"; do
    echo "测试 ${GC_NAMES[$i]}..."
    echo "=== ${GC_NAMES[$i]} ===" >> $RESULT_FILE
    
    # 运行测试并记录结果
    java ${GC_CONFIGS[$i]} \
         -XX:+PrintGC \
         -XX:+PrintGCDetails \
         -XX:+PrintGCTimeStamps \
         -cp $JAR_FILE $TEST_CLASS 2>&1 | tee -a $RESULT_FILE
    
    echo "" >> $RESULT_FILE
    echo "等待系统恢复..."
    sleep 5
done

echo "测试完成,结果保存在 $RESULT_FILE"

本章总结

关键要点

  1. 垃圾回收基础

    • 理解GC的目标和挑战
    • 掌握主要GC算法原理
    • 了解分代垃圾回收机制
  2. VisualVM GC监控

    • 使用Monitor标签页监控GC活动
    • 安装和使用Visual GC插件
    • 解读GC性能指标
  3. GC日志分析

    • 配置GC日志输出
    • 分析GC日志内容
    • 识别GC性能问题
  4. GC调优策略

    • 选择合适的垃圾回收器
    • 调整JVM参数
    • 监控和验证调优效果

最佳实践

  1. 监控策略

    • 建立GC性能基线
    • 持续监控GC指标
    • 设置合理的告警阈值
  2. 调优原则

    • 明确性能目标(吞吐量 vs 延迟)
    • 渐进式调优,避免大幅度改动
    • 充分测试验证调优效果
  3. 性能优化

    • 减少对象创建和生命周期
    • 合理设置堆大小
    • 选择适合的GC算法
  4. 故障诊断

    • 分析GC日志定位问题
    • 使用VisualVM进行实时监控
    • 结合应用日志进行综合分析

下一章预告

下一章我们将学习应用程序性能分析,包括:

  • 应用程序性能指标
  • 性能瓶颈识别
  • 数据库连接池监控
  • 缓存性能分析
  • 性能优化策略

通过前面几章的学习,我们已经掌握了VisualVM的核心功能。下一章将把这些技能应用到实际的应用程序性能分析中,帮助您成为Java性能调优专家。

1.2.2 复制算法(Copying)
java 复制代码
// 复制算法演示
import java.util.*;

public class CopyingAlgorithmDemo {
    
    private static final int HEAP_SIZE = 1000;
    private static Object[] fromSpace = new Object[HEAP_SIZE / 2];
    private static Object[] toSpace = new Object[HEAP_SIZE / 2];
    private static int fromPointer = 0;
    private static int toPointer = 0;
    
    public static void main(String[] args) {
        System.out.println("=== Copying Algorithm Demo ===");
        
        // 分配对象到 from 空间
        allocateObjects();
        
        // 执行复制 GC
        performCopyingGC();
        
        System.out.println("Copying GC completed");
    }
    
    private static void allocateObjects() {
        System.out.println("Allocating objects in from-space...");
        
        // 模拟对象分配
        for (int i = 0; i < 200; i++) {
            if (fromPointer < fromSpace.length) {
                fromSpace[fromPointer++] = new TestObject("Object_" + i);
            }
        }
        
        System.out.println("Allocated " + fromPointer + " objects");
    }
    
    private static void performCopyingGC() {
        System.out.println("\n=== Copying GC ===");
        
        // 复制存活对象到 to 空间
        toPointer = 0;
        
        for (int i = 0; i < fromPointer; i++) {
            Object obj = fromSpace[i];
            if (obj != null && isAlive(obj)) {
                // 复制到 to 空间
                toSpace[toPointer++] = obj;
                System.out.println("Copied: " + obj);
            }
        }
        
        // 交换空间
        Object[] temp = fromSpace;
        fromSpace = toSpace;
        toSpace = temp;
        
        // 重置指针
        fromPointer = toPointer;
        toPointer = 0;
        
        // 清空 to 空间(原 from 空间)
        Arrays.fill(toSpace, null);
        
        System.out.println("Copied " + fromPointer + " live objects");
        System.out.println("From-space and to-space swapped");
    }
    
    private static boolean isAlive(Object obj) {
        // 简单的存活判断逻辑
        if (obj instanceof TestObject) {
            TestObject testObj = (TestObject) obj;
            // 假设偶数 ID 的对象存活
            return testObj.getId() % 2 == 0;
        }
        return false;
    }
    
    static class TestObject {
        private final String name;
        private final int id;
        
        public TestObject(String name) {
            this.name = name;
            this.id = Integer.parseInt(name.split("_")[1]);
        }
        
        public int getId() {
            return id;
        }
        
        @Override
        public String toString() {
            return "TestObject{" + name + "}";
        }
    }
}
1.2.3 标记-整理算法(Mark-Compact)
java 复制代码
// 标记-整理算法演示
import java.util.*;

public class MarkCompactDemo {
    
    private static final int HEAP_SIZE = 1000;
    private static Object[] heap = new Object[HEAP_SIZE];
    private static boolean[] marked = new boolean[HEAP_SIZE];
    private static int heapPointer = 0;
    
    public static void main(String[] args) {
        System.out.println("=== Mark-Compact Algorithm Demo ===");
        
        // 分配对象
        allocateObjects();
        
        // 执行标记-整理 GC
        performMarkCompactGC();
        
        System.out.println("Mark-Compact GC completed");
    }
    
    private static void allocateObjects() {
        System.out.println("Allocating objects...");
        
        // 分配对象,模拟内存碎片
        for (int i = 0; i < 300; i++) {
            if (heapPointer < heap.length) {
                heap[heapPointer++] = new CompactObject("Object_" + i);
            }
        }
        
        // 模拟一些对象变为垃圾
        for (int i = 0; i < heapPointer; i += 3) {
            heap[i] = null; // 每三个对象中删除一个
        }
        
        System.out.println("Initial heap state:");
        printHeapState();
    }
    
    private static void performMarkCompactGC() {
        System.out.println("\n=== Mark-Compact GC ===");
        
        // 标记阶段
        markPhase();
        
        // 整理阶段
        compactPhase();
        
        System.out.println("\nFinal heap state:");
        printHeapState();
    }
    
    private static void markPhase() {
        System.out.println("Mark phase...");
        
        Arrays.fill(marked, false);
        
        // 标记存活对象
        for (int i = 0; i < heapPointer; i++) {
            if (heap[i] != null) {
                marked[i] = true;
            }
        }
        
        int markedCount = 0;
        for (boolean mark : marked) {
            if (mark) markedCount++;
        }
        
        System.out.println("Marked " + markedCount + " objects");
    }
    
    private static void compactPhase() {
        System.out.println("Compact phase...");
        
        int writeIndex = 0;
        
        // 将存活对象移动到堆的开始位置
        for (int readIndex = 0; readIndex < heapPointer; readIndex++) {
            if (marked[readIndex] && heap[readIndex] != null) {
                if (readIndex != writeIndex) {
                    heap[writeIndex] = heap[readIndex];
                    heap[readIndex] = null;
                    System.out.println("Moved " + heap[writeIndex] + 
                                     " from " + readIndex + " to " + writeIndex);
                }
                writeIndex++;
            }
        }
        
        // 清空剩余空间
        for (int i = writeIndex; i < heapPointer; i++) {
            heap[i] = null;
        }
        
        heapPointer = writeIndex;
        System.out.println("Compacted to " + heapPointer + " objects");
    }
    
    private static void printHeapState() {
        System.out.print("Heap: [");
        for (int i = 0; i < Math.min(20, heap.length); i++) {
            if (heap[i] != null) {
                System.out.print("O");
            } else {
                System.out.print(".");
            }
        }
        System.out.println("] (showing first 20 slots)");
        
        int liveObjects = 0;
        for (Object obj : heap) {
            if (obj != null) liveObjects++;
        }
        System.out.println("Live objects: " + liveObjects + "/" + heapPointer);
    }
    
    static class CompactObject {
        private final String name;
        
        public CompactObject(String name) {
            this.name = name;
        }
        
        @Override
        public String toString() {
            return name;
        }
    }
}

1.3 分代垃圾回收

1.3.1 分代假设

分代垃圾回收基于以下观察:

  • 弱分代假设:大多数对象很快变为垃圾
  • 强分代假设:存活时间长的对象倾向于继续存活
  • 跨代引用稀少:老年代对象很少引用新生代对象
1.3.2 分代结构
java 复制代码
// 分代垃圾回收演示
import java.util.*;
import java.util.concurrent.*;

public class GenerationalGCDemo {
    
    // 模拟分代结构
    private static final YoungGeneration youngGen = new YoungGeneration();
    private static final OldGeneration oldGen = new OldGeneration();
    private static final List<Object> roots = new ArrayList<>();
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Generational GC Demo ===");
        
        // 模拟对象分配和 GC
        simulateAllocation();
        
        System.out.println("Generational GC simulation completed");
    }
    
    private static void simulateAllocation() throws InterruptedException {
        Random random = new Random();
        
        for (int cycle = 0; cycle < 10; cycle++) {
            System.out.println("\n=== Allocation Cycle " + (cycle + 1) + " ===");
            
            // 分配短生命周期对象
            for (int i = 0; i < 100; i++) {
                GenerationalObject obj = new GenerationalObject(
                    "Short_" + cycle + "_" + i, 
                    ObjectType.SHORT_LIVED
                );
                youngGen.allocate(obj);
                
                // 10% 的对象成为根对象
                if (random.nextDouble() < 0.1) {
                    roots.add(obj);
                }
            }
            
            // 分配中等生命周期对象
            for (int i = 0; i < 20; i++) {
                GenerationalObject obj = new GenerationalObject(
                    "Medium_" + cycle + "_" + i, 
                    ObjectType.MEDIUM_LIVED
                );
                youngGen.allocate(obj);
                
                if (random.nextDouble() < 0.3) {
                    roots.add(obj);
                }
            }
            
            // 分配长生命周期对象
            for (int i = 0; i < 5; i++) {
                GenerationalObject obj = new GenerationalObject(
                    "Long_" + cycle + "_" + i, 
                    ObjectType.LONG_LIVED
                );
                youngGen.allocate(obj);
                roots.add(obj); // 长生命周期对象都是根对象
            }
            
            // 检查是否需要 Minor GC
            if (youngGen.needsGC()) {
                performMinorGC();
            }
            
            // 检查是否需要 Major GC
            if (oldGen.needsGC()) {
                performMajorGC();
            }
            
            // 模拟一些根对象失效
            if (cycle > 2) {
                removeOldRoots(random);
            }
            
            Thread.sleep(100); // 模拟时间流逝
        }
    }
    
    private static void performMinorGC() {
        System.out.println("\n--- Minor GC ---");
        
        Set<GenerationalObject> survivors = new HashSet<>();
        
        // 从根对象标记年轻代中的存活对象
        for (Object root : roots) {
            if (root instanceof GenerationalObject) {
                GenerationalObject obj = (GenerationalObject) root;
                if (youngGen.contains(obj)) {
                    markSurvivors(obj, survivors);
                }
            }
        }
        
        // 处理存活对象
        List<GenerationalObject> promoted = new ArrayList<>();
        for (GenerationalObject survivor : survivors) {
            survivor.incrementAge();
            
            // 年龄达到阈值的对象晋升到老年代
            if (survivor.getAge() >= 3) {
                promoted.add(survivor);
            }
        }
        
        // 晋升对象到老年代
        for (GenerationalObject obj : promoted) {
            youngGen.remove(obj);
            oldGen.add(obj);
            System.out.println("Promoted to old generation: " + obj);
        }
        
        // 清理年轻代
        youngGen.clear();
        
        // 将剩余存活对象放回年轻代
        for (GenerationalObject survivor : survivors) {
            if (!promoted.contains(survivor)) {
                youngGen.allocate(survivor);
            }
        }
        
        System.out.println("Minor GC completed. Promoted: " + promoted.size() + 
                         ", Survivors: " + (survivors.size() - promoted.size()));
    }
    
    private static void performMajorGC() {
        System.out.println("\n--- Major GC ---");
        
        Set<GenerationalObject> allSurvivors = new HashSet<>();
        
        // 标记所有代中的存活对象
        for (Object root : roots) {
            if (root instanceof GenerationalObject) {
                markSurvivors((GenerationalObject) root, allSurvivors);
            }
        }
        
        // 清理老年代
        oldGen.retainAll(allSurvivors);
        
        // 清理年轻代
        youngGen.clear();
        for (GenerationalObject survivor : allSurvivors) {
            if (!oldGen.contains(survivor)) {
                youngGen.allocate(survivor);
            }
        }
        
        System.out.println("Major GC completed. Total survivors: " + allSurvivors.size());
    }
    
    private static void markSurvivors(GenerationalObject obj, Set<GenerationalObject> survivors) {
        if (obj == null || survivors.contains(obj)) {
            return;
        }
        
        survivors.add(obj);
        
        // 递归标记引用的对象
        for (GenerationalObject ref : obj.getReferences()) {
            markSurvivors(ref, survivors);
        }
    }
    
    private static void removeOldRoots(Random random) {
        // 移除一些旧的根对象
        Iterator<Object> iterator = roots.iterator();
        while (iterator.hasNext()) {
            Object root = iterator.next();
            if (root instanceof GenerationalObject) {
                GenerationalObject obj = (GenerationalObject) root;
                
                // 根据对象类型决定移除概率
                double removeProb = switch (obj.getType()) {
                    case SHORT_LIVED -> 0.8;
                    case MEDIUM_LIVED -> 0.3;
                    case LONG_LIVED -> 0.05;
                };
                
                if (random.nextDouble() < removeProb) {
                    iterator.remove();
                }
            }
        }
    }
    
    // 年轻代实现
    static class YoungGeneration {
        private final List<GenerationalObject> objects = new ArrayList<>();
        private static final int MAX_SIZE = 200;
        
        public void allocate(GenerationalObject obj) {
            objects.add(obj);
        }
        
        public boolean needsGC() {
            return objects.size() > MAX_SIZE;
        }
        
        public boolean contains(GenerationalObject obj) {
            return objects.contains(obj);
        }
        
        public void remove(GenerationalObject obj) {
            objects.remove(obj);
        }
        
        public void clear() {
            objects.clear();
        }
        
        public int size() {
            return objects.size();
        }
    }
    
    // 老年代实现
    static class OldGeneration {
        private final Set<GenerationalObject> objects = new HashSet<>();
        private static final int MAX_SIZE = 500;
        
        public void add(GenerationalObject obj) {
            objects.add(obj);
        }
        
        public boolean needsGC() {
            return objects.size() > MAX_SIZE;
        }
        
        public boolean contains(GenerationalObject obj) {
            return objects.contains(obj);
        }
        
        public void retainAll(Set<GenerationalObject> survivors) {
            objects.retainAll(survivors);
        }
        
        public int size() {
            return objects.size();
        }
    }
    
    // 对象类型枚举
    enum ObjectType {
        SHORT_LIVED, MEDIUM_LIVED, LONG_LIVED
    }
    
    // 分代对象
    static class GenerationalObject {
        private final String name;
        private final ObjectType type;
        private int age = 0;
        private final List<GenerationalObject> references = new ArrayList<>();
        
        public GenerationalObject(String name, ObjectType type) {
            this.name = name;
            this.type = type;
        }
        
        public void incrementAge() {
            age++;
        }
        
        public int getAge() {
            return age;
        }
        
        public ObjectType getType() {
            return type;
        }
        
        public List<GenerationalObject> getReferences() {
            return references;
        }
        
        public void addReference(GenerationalObject obj) {
            references.add(obj);
        }
        
        @Override
        public String toString() {
            return name + "(age=" + age + ", type=" + type + ")";
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof GenerationalObject)) return false;
            GenerationalObject that = (GenerationalObject) o;
            return Objects.equals(name, that.name);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }
}

2. VisualVM 垃圾回收监控

2.1 GC 监控界面

2.1.1 Monitor 标签页的 GC 信息

VisualVM 的 Monitor 标签页提供了实时的垃圾回收监控信息:

  • Heap Size:堆内存大小变化
  • Used Heap:已使用堆内存
  • GC Activity:垃圾回收活动图表
  • Generations:分代内存使用情况
2.1.2 GC 性能指标
java 复制代码
// GC 监控示例程序
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.*;

public class GCMonitoringExample {
    
    private static final List<Object> memoryConsumers = new ArrayList<>();
    private static final Random random = new Random();
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== GC Monitoring Example ===");
        System.out.println("PID: " + ProcessHandle.current().pid());
        System.out.println("Monitor this process with VisualVM");
        
        // 启动 GC 监控
        startGCMonitoring();
        
        // 创建不同的内存使用模式
        simulateMemoryPatterns();
    }
    
    private static void startGCMonitoring() {
        ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
        
        monitor.scheduleAtFixedRate(() -> {
            printGCStats();
        }, 0, 5, TimeUnit.SECONDS);
    }
    
    private static void printGCStats() {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
        
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        
        System.out.println("\n=== GC Statistics ===");
        System.out.printf("Heap: %d MB / %d MB (%.1f%%)%n",
            heapUsage.getUsed() / 1024 / 1024,
            heapUsage.getMax() / 1024 / 1024,
            (double) heapUsage.getUsed() / heapUsage.getMax() * 100);
        
        for (GarbageCollectorMXBean gcBean : gcBeans) {
            System.out.printf("%s: %d collections, %d ms total%n",
                gcBean.getName(),
                gcBean.getCollectionCount(),
                gcBean.getCollectionTime());
        }
    }
    
    private static void simulateMemoryPatterns() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // 模式 1:频繁的小对象分配
        executor.submit(() -> {
            try {
                frequentSmallAllocations();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 模式 2:大对象分配
        executor.submit(() -> {
            try {
                largeObjectAllocations();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 模式 3:内存泄漏模拟
        executor.submit(() -> {
            try {
                memoryLeakSimulation();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 运行 10 分钟
        Thread.sleep(600_000);
        
        executor.shutdownNow();
        System.out.println("Memory pattern simulation completed");
    }
    
    private static void frequentSmallAllocations() throws InterruptedException {
        while (!Thread.currentThread().isInterrupted()) {
            // 创建大量小对象
            List<String> tempList = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                tempList.add("TempString_" + i + "_" + System.nanoTime());
            }
            
            // 偶尔保留一些对象
            if (random.nextDouble() < 0.1) {
                memoryConsumers.add(tempList);
            }
            
            Thread.sleep(10);
        }
    }
    
    private static void largeObjectAllocations() throws InterruptedException {
        while (!Thread.currentThread().isInterrupted()) {
            // 创建大对象
            byte[] largeArray = new byte[1024 * 1024]; // 1MB
            Arrays.fill(largeArray, (byte) random.nextInt(256));
            
            // 偶尔保留大对象
            if (random.nextDouble() < 0.05) {
                memoryConsumers.add(largeArray);
            }
            
            Thread.sleep(500);
        }
    }
    
    private static void memoryLeakSimulation() throws InterruptedException {
        Map<String, Object> leakyMap = new HashMap<>();
        int counter = 0;
        
        while (!Thread.currentThread().isInterrupted()) {
            // 模拟内存泄漏:不断添加对象但很少移除
            String key = "leak_" + (counter++);
            Object value = new LargeObject(key);
            leakyMap.put(key, value);
            
            // 偶尔清理一些旧对象
            if (counter % 1000 == 0) {
                Iterator<String> iterator = leakyMap.keySet().iterator();
                for (int i = 0; i < 100 && iterator.hasNext(); i++) {
                    iterator.next();
                    iterator.remove();
                }
            }
            
            Thread.sleep(50);
        }
    }
    
    static class LargeObject {
        private final String id;
        private final byte[] data;
        private final List<String> metadata;
        
        public LargeObject(String id) {
            this.id = id;
            this.data = new byte[10240]; // 10KB
            this.metadata = new ArrayList<>();
            
            // 填充数据
            Arrays.fill(data, (byte) id.hashCode());
            
            // 添加元数据
            for (int i = 0; i < 100; i++) {
                metadata.add("metadata_" + id + "_" + i);
            }
        }
        
        public String getId() {
            return id;
        }
        
        @Override
        public String toString() {
            return "LargeObject{id='" + id + "', size=" + data.length + "}";
        }
    }
}

2.2 Visual GC 插件

2.2.1 安装 Visual GC 插件
  1. 打开 VisualVM
  2. 选择 Tools → Plugins
  3. 在 Available Plugins 中找到 Visual GC
  4. 点击 Install 安装插件
  5. 重启 VisualVM
2.2.2 Visual GC 界面解读

Visual GC 插件提供了详细的垃圾回收可视化信息:

  • Spaces:各个内存区域的使用情况
  • Graphs:实时的内存使用图表
  • Histogram:对象年龄分布
  • Details:详细的 GC 统计信息
java 复制代码
// Visual GC 演示程序
import java.util.*;
import java.util.concurrent.*;

public class VisualGCDemo {
    
    private static final List<Object> youngObjects = new ArrayList<>();
    private static final List<Object> oldObjects = new ArrayList<>();
    private static final Random random = new Random();
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Visual GC Demo ===");
        System.out.println("PID: " + ProcessHandle.current().pid());
        System.out.println("Use VisualVM with Visual GC plugin to monitor");
        
        // 创建不同生命周期的对象
        createGenerationalObjects();
        
        System.out.println("Visual GC demo completed");
    }
    
    private static void createGenerationalObjects() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // 创建短生命周期对象(主要在年轻代)
        executor.submit(() -> {
            try {
                createYoungGenerationObjects();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 创建长生命周期对象(会晋升到老年代)
        executor.submit(() -> {
            try {
                createOldGenerationObjects();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 运行 5 分钟
        Thread.sleep(300_000);
        
        executor.shutdownNow();
    }
    
    private static void createYoungGenerationObjects() throws InterruptedException {
        while (!Thread.currentThread().isInterrupted()) {
            // 创建大量短生命周期对象
            for (int i = 0; i < 1000; i++) {
                YoungObject obj = new YoungObject("young_" + i);
                youngObjects.add(obj);
                
                // 快速释放大部分对象
                if (youngObjects.size() > 5000) {
                    youngObjects.subList(0, 4000).clear();
                }
            }
            
            Thread.sleep(100);
        }
    }
    
    private static void createOldGenerationObjects() throws InterruptedException {
        while (!Thread.currentThread().isInterrupted()) {
            // 创建长生命周期对象
            OldObject obj = new OldObject("old_" + System.currentTimeMillis());
            oldObjects.add(obj);
            
            // 偶尔清理一些旧对象
            if (oldObjects.size() > 1000 && random.nextDouble() < 0.1) {
                oldObjects.remove(0);
            }
            
            Thread.sleep(1000);
        }
    }
    
    static class YoungObject {
        private final String name;
        private final byte[] data;
        
        public YoungObject(String name) {
            this.name = name;
            this.data = new byte[1024]; // 1KB
        }
        
        @Override
        public String toString() {
            return "YoungObject{" + name + "}";
        }
    }
    
    static class OldObject {
        private final String name;
        private final byte[] data;
        private final List<String> references;
        
        public OldObject(String name) {
            this.name = name;
            this.data = new byte[10240]; // 10KB
            this.references = new ArrayList<>();
            
            // 添加一些引用
            for (int i = 0; i < 50; i++) {
                references.add("ref_" + name + "_" + i);
            }
        }
        
        @Override
        public String toString() {
            return "OldObject{" + name + "}";
        }
    }
}

3. GC 日志分析

3.1 启用 GC 日志

3.1.1 JVM 参数配置
bash 复制代码
# Java 8 及之前版本
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:gc.log

# Java 9 及之后版本
-Xlog:gc*:gc.log:time,tags
3.1.2 GC 日志示例程序
java 复制代码
// GC 日志生成程序
import java.util.*;
import java.util.concurrent.*;

public class GCLogGenerator {
    
    private static final List<Object> memoryHolder = new ArrayList<>();
    private static final Random random = new Random();
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== GC Log Generator ===");
        System.out.println("Run with GC logging enabled:");
        System.out.println("Java 8: -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log");
        System.out.println("Java 11+: -Xlog:gc*:gc.log:time,tags");
        
        // 生成不同类型的 GC 事件
        generateGCEvents();
        
        System.out.println("GC log generation completed");
    }
    
    private static void generateGCEvents() throws InterruptedException {
        // 阶段 1:触发 Minor GC
        System.out.println("\nPhase 1: Triggering Minor GC...");
        triggerMinorGC();
        
        Thread.sleep(2000);
        
        // 阶段 2:触发 Major GC
        System.out.println("\nPhase 2: Triggering Major GC...");
        triggerMajorGC();
        
        Thread.sleep(2000);
        
        // 阶段 3:内存压力测试
        System.out.println("\nPhase 3: Memory pressure test...");
        memoryPressureTest();
        
        Thread.sleep(2000);
        
        // 阶段 4:清理内存
        System.out.println("\nPhase 4: Memory cleanup...");
        memoryHolder.clear();
        System.gc(); // 建议进行垃圾回收
    }
    
    private static void triggerMinorGC() throws InterruptedException {
        // 快速分配大量小对象,触发 Minor GC
        for (int i = 0; i < 10; i++) {
            List<String> tempObjects = new ArrayList<>();
            
            for (int j = 0; j < 100000; j++) {
                tempObjects.add("MinorGC_" + i + "_" + j);
            }
            
            // 保留少量对象
            if (i % 3 == 0) {
                memoryHolder.add(tempObjects.subList(0, 1000));
            }
            
            Thread.sleep(100);
        }
    }
    
    private static void triggerMajorGC() throws InterruptedException {
        // 分配大量长生命周期对象,触发 Major GC
        for (int i = 0; i < 100; i++) {
            LongLivedObject obj = new LongLivedObject("MajorGC_" + i);
            memoryHolder.add(obj);
            
            // 创建一些临时的大对象
            byte[] largeArray = new byte[1024 * 1024]; // 1MB
            Arrays.fill(largeArray, (byte) i);
            
            Thread.sleep(50);
        }
    }
    
    private static void memoryPressureTest() throws InterruptedException {
        // 创建内存压力,观察 GC 行为
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        for (int i = 0; i < 4; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try {
                    createMemoryPressure(threadId);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        Thread.sleep(30000); // 运行 30 秒
        executor.shutdownNow();
    }
    
    private static void createMemoryPressure(int threadId) throws InterruptedException {
        List<Object> localObjects = new ArrayList<>();
        
        while (!Thread.currentThread().isInterrupted()) {
            // 分配不同大小的对象
            for (int i = 0; i < 100; i++) {
                Object obj;
                
                if (random.nextDouble() < 0.7) {
                    // 70% 小对象
                    obj = new SmallObject("thread_" + threadId + "_small_" + i);
                } else if (random.nextDouble() < 0.9) {
                    // 20% 中等对象
                    obj = new MediumObject("thread_" + threadId + "_medium_" + i);
                } else {
                    // 10% 大对象
                    obj = new LargeObject("thread_" + threadId + "_large_" + i);
                }
                
                localObjects.add(obj);
            }
            
            // 随机清理一些对象
            if (localObjects.size() > 1000) {
                int removeCount = random.nextInt(500);
                for (int i = 0; i < removeCount && !localObjects.isEmpty(); i++) {
                    localObjects.remove(random.nextInt(localObjects.size()));
                }
            }
            
            Thread.sleep(10);
        }
    }
    
    static class SmallObject {
        private final String name;
        private final int[] data = new int[10]; // 40 bytes
        
        public SmallObject(String name) {
            this.name = name;
            Arrays.fill(data, name.hashCode());
        }
    }
    
    static class MediumObject {
        private final String name;
        private final byte[] data = new byte[1024]; // 1KB
        
        public MediumObject(String name) {
            this.name = name;
            Arrays.fill(data, (byte) name.hashCode());
        }
    }
    
    static class LargeObject {
        private final String name;
        private final byte[] data = new byte[10240]; // 10KB
        
        public LargeObject(String name) {
            this.name = name;
            Arrays.fill(data, (byte) name.hashCode());
        }
    }
    
    static class LongLivedObject {
        private final String name;
        private final Map<String, Object> properties = new HashMap<>();
        private final List<String> history = new ArrayList<>();
        
        public LongLivedObject(String name) {
            this.name = name;
            
            // 添加属性
            for (int i = 0; i < 50; i++) {
                properties.put("prop_" + i, "value_" + i + "_" + name);
            }
            
            // 添加历史记录
            for (int i = 0; i < 100; i++) {
                history.add("event_" + i + "_" + System.currentTimeMillis());
            }
        }
        
        @Override
        public String toString() {
            return "LongLivedObject{" + name + "}";
        }
    }
}

3.2 GC 日志解读

3.2.1 日志格式解析
java 复制代码
// GC 日志解析工具
import java.io.*;
import java.nio.file.*;
import java.time.*;
import java.time.format.*;
import java.util.*;
import java.util.regex.*;

public class GCLogAnalyzer {
    
    private static final Pattern GC_PATTERN = Pattern.compile(
        "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}[+-]\\d{4}): " +
        "\\[(\\d+\\.\\d+)s\\]\\[info\\]\\[gc\\s*\\] " +
        "GC\\((\\d+)\\) (.+)"
    );
    
    private static final Pattern PAUSE_PATTERN = Pattern.compile(
        "Pause (.+?) (\\d+)M->(\\d+)M\\((\\d+)M\\) (\\d+\\.\\d+)ms"
    );
    
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java GCLogAnalyzer <gc-log-file>");
            return;
        }
        
        String logFile = args[0];
        
        try {
            analyzeGCLog(logFile);
        } catch (IOException e) {
            System.err.println("Error reading GC log: " + e.getMessage());
        }
    }
    
    private static void analyzeGCLog(String logFile) throws IOException {
        System.out.println("=== GC Log Analysis ===");
        System.out.println("Analyzing: " + logFile);
        
        List<GCEvent> events = parseGCLog(logFile);
        
        if (events.isEmpty()) {
            System.out.println("No GC events found in log file");
            return;
        }
        
        // 分析统计信息
        analyzeStatistics(events);
        
        // 分析趋势
        analyzeTrends(events);
        
        // 识别问题
        identifyIssues(events);
    }
    
    private static List<GCEvent> parseGCLog(String logFile) throws IOException {
        List<GCEvent> events = new ArrayList<>();
        List<String> lines = Files.readAllLines(Paths.get(logFile));
        
        for (String line : lines) {
            GCEvent event = parseGCEvent(line);
            if (event != null) {
                events.add(event);
            }
        }
        
        System.out.println("Parsed " + events.size() + " GC events");
        return events;
    }
    
    private static GCEvent parseGCEvent(String line) {
        Matcher gcMatcher = GC_PATTERN.matcher(line);
        if (!gcMatcher.find()) {
            return null;
        }
        
        String timestamp = gcMatcher.group(1);
        double uptime = Double.parseDouble(gcMatcher.group(2));
        int gcId = Integer.parseInt(gcMatcher.group(3));
        String details = gcMatcher.group(4);
        
        Matcher pauseMatcher = PAUSE_PATTERN.matcher(details);
        if (pauseMatcher.find()) {
            String gcType = pauseMatcher.group(1);
            int beforeMB = Integer.parseInt(pauseMatcher.group(2));
            int afterMB = Integer.parseInt(pauseMatcher.group(3));
            int totalMB = Integer.parseInt(pauseMatcher.group(4));
            double pauseMs = Double.parseDouble(pauseMatcher.group(5));
            
            return new GCEvent(timestamp, uptime, gcId, gcType, 
                             beforeMB, afterMB, totalMB, pauseMs);
        }
        
        return null;
    }
    
    private static void analyzeStatistics(List<GCEvent> events) {
        System.out.println("\n=== GC Statistics ===");
        
        Map<String, List<GCEvent>> eventsByType = new HashMap<>();
        for (GCEvent event : events) {
            eventsByType.computeIfAbsent(event.getType(), k -> new ArrayList<>()).add(event);
        }
        
        for (Map.Entry<String, List<GCEvent>> entry : eventsByType.entrySet()) {
            String type = entry.getKey();
            List<GCEvent> typeEvents = entry.getValue();
            
            double totalPause = typeEvents.stream().mapToDouble(GCEvent::getPauseMs).sum();
            double avgPause = totalPause / typeEvents.size();
            double maxPause = typeEvents.stream().mapToDouble(GCEvent::getPauseMs).max().orElse(0);
            double minPause = typeEvents.stream().mapToDouble(GCEvent::getPauseMs).min().orElse(0);
            
            System.out.printf("%s GC:%n", type);
            System.out.printf("  Count: %d%n", typeEvents.size());
            System.out.printf("  Total pause: %.2f ms%n", totalPause);
            System.out.printf("  Average pause: %.2f ms%n", avgPause);
            System.out.printf("  Max pause: %.2f ms%n", maxPause);
            System.out.printf("  Min pause: %.2f ms%n", minPause);
            System.out.println();
        }
    }
    
    private static void analyzeTrends(List<GCEvent> events) {
        System.out.println("=== GC Trends ===");
        
        if (events.size() < 10) {
            System.out.println("Not enough events for trend analysis");
            return;
        }
        
        // 分析暂停时间趋势
        List<Double> pauseTimes = events.stream()
            .map(GCEvent::getPauseMs)
            .toList();
        
        double trend = calculateTrend(pauseTimes);
        
        if (trend > 0.1) {
            System.out.println("⚠️  GC pause times are increasing (trend: +" + 
                             String.format("%.2f", trend) + " ms per GC)");
        } else if (trend < -0.1) {
            System.out.println("✅ GC pause times are decreasing (trend: " + 
                             String.format("%.2f", trend) + " ms per GC)");
        } else {
            System.out.println("➡️  GC pause times are stable");
        }
        
        // 分析内存使用趋势
        List<Double> memoryUsage = events.stream()
            .map(event -> (double) event.getAfterMB() / event.getTotalMB())
            .toList();
        
        double memoryTrend = calculateTrend(memoryUsage);
        
        if (memoryTrend > 0.01) {
            System.out.println("⚠️  Memory usage is increasing (possible memory leak)");
        } else {
            System.out.println("✅ Memory usage is stable");
        }
    }
    
    private static double calculateTrend(List<Double> values) {
        if (values.size() < 2) return 0;
        
        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        int n = values.size();
        
        for (int i = 0; i < n; i++) {
            double x = i;
            double y = values.get(i);
            
            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumX2 += x * x;
        }
        
        return (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    }
    
    private static void identifyIssues(List<GCEvent> events) {
        System.out.println("\n=== Issue Detection ===");
        
        // 检查长暂停时间
        List<GCEvent> longPauses = events.stream()
            .filter(event -> event.getPauseMs() > 100) // 超过 100ms
            .toList();
        
        if (!longPauses.isEmpty()) {
            System.out.println("⚠️  Found " + longPauses.size() + " GC events with pause > 100ms:");
            longPauses.forEach(event -> 
                System.out.printf("  %s: %.2f ms%n", event.getType(), event.getPauseMs()));
        }
        
        // 检查频繁 GC
        double avgInterval = calculateAverageInterval(events);
        if (avgInterval < 1.0) { // 平均间隔小于 1 秒
            System.out.println("⚠️  Frequent GC detected (average interval: " + 
                             String.format("%.2f", avgInterval) + " seconds)");
        }
        
        // 检查内存回收效率
        double avgReclaimed = events.stream()
            .mapToDouble(event -> (double)(event.getBeforeMB() - event.getAfterMB()) / event.getBeforeMB())
            .average()
            .orElse(0);
        
        if (avgReclaimed < 0.1) { // 平均回收率小于 10%
            System.out.println("⚠️  Low GC efficiency detected (average reclaimed: " + 
                             String.format("%.1f%%", avgReclaimed * 100) + ")");
        }
        
        if (longPauses.isEmpty() && avgInterval >= 1.0 && avgReclaimed >= 0.1) {
            System.out.println("✅ No significant GC issues detected");
        }
    }
    
    private static double calculateAverageInterval(List<GCEvent> events) {
        if (events.size() < 2) return Double.MAX_VALUE;
        
        double totalInterval = 0;
        for (int i = 1; i < events.size(); i++) {
            totalInterval += events.get(i).getUptime() - events.get(i-1).getUptime();
        }
        
        return totalInterval / (events.size() - 1);
    }
    
    static class GCEvent {
        private final String timestamp;
        private final double uptime;
        private final int gcId;
        private final String type;
        private final int beforeMB;
        private final int afterMB;
        private final int totalMB;
        private final double pauseMs;
        
        public GCEvent(String timestamp, double uptime, int gcId, String type,
                      int beforeMB, int afterMB, int totalMB, double pauseMs) {
            this.timestamp = timestamp;
            this.uptime = uptime;
            this.gcId = gcId;
            this.type = type;
            this.beforeMB = beforeMB;
            this.afterMB = afterMB;
            this.totalMB = totalMB;
            this.pauseMs = pauseMs;
        }
        
        // Getters
        public String getTimestamp() { return timestamp; }
        public double getUptime() { return uptime; }
        public int getGcId() { return gcId; }
        public String getType() { return type; }
        public int getBeforeMB() { return beforeMB; }
        public int getAfterMB() { return afterMB; }
        public int getTotalMB() { return totalMB; }
        public double getPauseMs() { return pauseMs; }
        
        @Override
        public String toString() {
            return String.format("GC(%d) %s: %dM->%dM(%dM) %.2fms", 
                               gcId, type, beforeMB, afterMB, totalMB, pauseMs);
        }
    }
}

4. 垃圾回收器选择与调优

4.1 垃圾回收器对比

4.1.1 Serial GC
java 复制代码
// Serial GC 演示
// JVM 参数: -XX:+UseSerialGC -Xmx512m -Xms512m

import java.util.*;
import java.util.concurrent.*;

public class SerialGCDemo {
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Serial GC Demo ===");
        System.out.println("Run with: -XX:+UseSerialGC -Xmx512m -Xms512m");
相关推荐
一行•坚书4 小时前
Redisson分布式锁会发生死锁问题吗?怎么发生的?
java·分布式·后端
尚久龙4 小时前
安卓学习 之 模拟登录界面
java·学习·手机·android studio·安卓
whatever who cares4 小时前
Android/Java 异常捕获
android·java·开发语言
野犬寒鸦5 小时前
力扣hot100:矩阵置零(73)(原地算法)
java·数据结构·后端·算法
PEI045 小时前
Java集合遍历的方法有哪些
java·windows·python
SimonKing5 小时前
你的图片又被别人“白嫖”了?用这篇Java防盗链攻略说再见!
java·后端·程序员
做科研的周师兄5 小时前
【机器学习入门】5.4 线性回归模型的应用——从CO₂浓度预测学透实战全流程
java·大数据·数据库·人工智能·机器学习·回归·线性回归
We....5 小时前
多线程同步安全机制
java·jvm·安全
David爱编程5 小时前
synchronized 的可重入性:避免死锁的隐藏武器
java·后端