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)
// 标记-清除算法演示
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;}@Overridepublic String toString() {return "Node{" + name + "}";}}
}

垃圾回收器选择与调优

常见垃圾回收器对比

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

GC 参数调优示例

// 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());// 预热JVMwarmUp();// 执行测试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(默认)
# 基本配置
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
# 基本配置
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+)
# 基本配置
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调优工具类

// 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压力测试

// 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.5MBlargeObjects.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配置的性能:

#!/bin/bash
# gc_comparison.shecho "=== 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[@]}"; doecho "测试 ${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_FILEecho "" >> $RESULT_FILEecho "等待系统恢复..."sleep 5
doneecho "测试完成,结果保存在 $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)
// 复制算法演示
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();// 执行复制 GCperformCopyingGC();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;}@Overridepublic String toString() {return "TestObject{" + name + "}";}}
}
1.2.3 标记-整理算法(Mark-Compact)
// 标记-整理算法演示
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();// 执行标记-整理 GCperformMarkCompactGC();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;}@Overridepublic String toString() {return name;}}
}

1.3 分代垃圾回收

1.3.1 分代假设

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

  • 弱分代假设:大多数对象很快变为垃圾
  • 强分代假设:存活时间长的对象倾向于继续存活
  • 跨代引用稀少:老年代对象很少引用新生代对象
1.3.2 分代结构
// 分代垃圾回收演示
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 ===");// 模拟对象分配和 GCsimulateAllocation();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 GCif (youngGen.needsGC()) {performMinorGC();}// 检查是否需要 Major GCif (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);}@Overridepublic String toString() {return name + "(age=" + age + ", type=" + type + ")";}@Overridepublic 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);}@Overridepublic 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 性能指标
// 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]; // 1MBArrays.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]; // 10KBthis.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;}@Overridepublic 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 统计信息
// 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}@Overridepublic 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]; // 10KBthis.references = new ArrayList<>();// 添加一些引用for (int i = 0; i < 50; i++) {references.add("ref_" + name + "_" + i);}}@Overridepublic String toString() {return "OldObject{" + name + "}";}}
}

3. GC 日志分析

3.1 启用 GC 日志

3.1.1 JVM 参数配置
# 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 日志示例程序
// 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 GCSystem.out.println("\nPhase 1: Triggering Minor GC...");triggerMinorGC();Thread.sleep(2000);// 阶段 2:触发 Major GCSystem.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 GCfor (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 GCfor (int i = 0; i < 100; i++) {LongLivedObject obj = new LongLivedObject("MajorGC_" + i);memoryHolder.add(obj);// 创建一些临时的大对象byte[] largeArray = new byte[1024 * 1024]; // 1MBArrays.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 bytespublic 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]; // 1KBpublic 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]; // 10KBpublic 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());}}@Overridepublic String toString() {return "LongLivedObject{" + name + "}";}}
}

3.2 GC 日志解读

3.2.1 日志格式解析
// 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()));}// 检查频繁 GCdouble 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;}// Getterspublic 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; }@Overridepublic 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
// Serial GC 演示
// JVM 参数: -XX:+UseSerialGC -Xmx512m -Xms512mimport 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");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95291.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95291.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/95291.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROS2核心模块-动作通信、参数服务

动作通信 机器人导航到某个目标点,此过程需要一个节点A发布目标信息&#xff0c;然后一个节点B接收到请求并控制移动&#xff0c;最终响应目标达成状态信息。 乍一看&#xff0c;这好像是服务通信实现&#xff0c;因为需求中要A发送目标&#xff0c;B执行并返回结果&#xff0c…

word文档封面中文件编号等标题和内容无法对齐

问题 word文档封面中文件编号等标题和内容无法对齐&#xff0c;因为标题使用的是底纹不是文件内容。 解决办法 字体大小、行距两者配合就可以解决。

163起融资,梅卡曼德融资额夺冠,钉钉、百度智能云10周年,汉桑科技IPO| 2025年8月人工智能投融资观察 · 极新月报

“ 二级的活跃会传导到一级吗&#xff1f;”文&#xff5c;云舒&小鱼编辑 | 小白出品&#xff5c;极新8月重点关注&#xff1a;1、八月人工智能领域投融资事件163起&#xff0c;披露金额76.8亿人民币。2、亿级人民币以上金额的投资事件共20起 。3、八月人工智能领域发生一起…

微信小程序预览和分享文件

预览文档previewFile(val) { let item val.currentTarget.dataset.item wx.downloadFile({url: item.filePath, // 替换为实际的文件地址success: function (res) {let filePath ${wx.env.USER_DATA_PATH}/${item.fileName}|| res.tempFilePath //查看的文件名wx.openDocumen…

开源 C++ QT Widget 开发(十二)图表--环境监测表盘

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源…

ARMv8架构01 - ARM64架构寄存器基础

一 、ARM64架构基础 1 ARMv8 A 架构介绍 ARMv8 - A是ARM公司发布的第一代支持64位处理器的指令集和架构。它在扩充64位寄存器的同时提供对上一代架构指令集的兼容&#xff0c;因而能同时提供运行 32位 和 64位应用程序的执行环境。 超大物理地址空间&#xff08;large Physical…

flutter专栏--深入剖析你的第一个flutter应用

使用fvm管理flutter版本 如果你有使用多版本flutter的需求&#xff0c;那么fvm将会给你提供较大的帮助。下面我列举一下mac flutter3.35.2的版本的操作命令&#xff0c;完成之后&#xff0c;你将可以随意切换flutter版本 # 下载fvm相关的依赖 brew tap leoafarias/fvm brew …

MongoDB 聚合查询超时:索引优化与分片策略的踩坑记录

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 &#x1f31f; Hello&#xff0c;我是Xxtaoaooo&#xff01; &#x1f308; “代码是逻辑的诗篇&#xff…

Augmentcode免费额度AI开发WordPress商城实战

Augment AI开发WordPress商城实战&#xff1a;从零构建到免费额度续杯完整指南 前言 在AI编程工具日益普及的今天&#xff0c;如何高效利用这些工具来开发实际项目成为了开发者关注的焦点。本文将详细介绍如何使用Augment AI从零开始构建一个功能完整的WordPress商城系统&#…

【C++八股文】数据结构篇

一、单例模式优化实现 原代码问题分析 ​内存序重排序风险​&#xff1a;双重检查锁在C中可能因指令重排导致半初始化对象被访问​锁粒度过大​&#xff1a;每次获取实例都需要加锁&#xff0c;影响性能​线程安全性不足​&#xff1a;未考虑C11前的内存模型问题 改进方案&a…

并发编程——15 线程池ForkJoinPool实战及其工作原理分析

1 一道算法题引发的思考及其实现 1.1 算法题 问&#xff1a;如何充分利用多核 CPU 的性能&#xff0c;快速对一个2千万大小的数组进行排序&#xff1f; 这道题可以通过归并排序来解决&#xff1b; 1.2 什么是归并排序&#xff1f; 归并排序&#xff08;Merge Sort&#xff…

Kafka面试精讲 Day 6:Kafka日志存储结构与索引机制

【Kafka面试精讲 Day 6】Kafka日志存储结构与索引机制 在“Kafka面试精讲”系列的第6天&#xff0c;我们将深入剖析 Kafka的日志存储结构与索引机制。这是Kafka高性能、高吞吐量背后的核心设计之一&#xff0c;也是中高级面试中的高频考点。面试官常通过这个问题考察候选人是否…

Linux 字符设备驱动框架学习记录(三)

Linux字符设备驱动开发新框架详解 一、新旧驱动框架对比 传统字符设备驱动流程 手动分配设备号 (register_chrdev_region)实现file_operations结构体使用mknod手动创建设备节点 新式驱动框架优势 自动设备号分配&#xff1a;动态申请避免冲突自动节点创建&#xff1a;通过class…

《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(1)

目 录 摘 要 一、研究背景与目的 1.1 介绍拒绝服务&#xff08;DoS&#xff09;和分布式拒绝服务&#xff08;DDoS&#xff09;攻击的背景 &#xff08;1&#xff09;拒绝服务攻击&#xff08;DoS&#xff09;  &#xff08;2&#xff09;分布式拒绝服务攻击&#xff0…

深度学习篇---模型组成部分

模型组成部分&#xff1a;在 PyTorch 框架下进行图像分类任务时&#xff0c;深度学习代码通常由几个核心部分组成。这些部分中有些可以在不同网络间复用&#xff0c;有些则需要根据具体任务或网络结构进行修改。下面我将用通俗易懂的方式介绍这些组成部分&#xff1a;1. 数据准…

关于ANDROUD APPIUM安装细则

1&#xff0c;可以先参考一下连接 PythonAppium自动化完整教程_appium python教程-CSDN博客 2&#xff0c;appium 需要对应的版本的node&#xff0c;可以用nvm对node 进行版本隔离 3&#xff0c;对应需要安装android stuido 和对应的sdk &#xff0c;按照以上连接进行下载安…

八、算法设计与分析

1 算法设计与分析的基本概念 1.1 算法 定义 &#xff1a;算法是对特定问题求解步骤的一种描述&#xff0c;是有限指令序列&#xff0c;每条指令表示一个或多个操作。特性 &#xff1a; 有穷性&#xff1a;算法需在有限步骤和时间内结束。确定性&#xff1a;指令无歧义&#xff…

机器学习从入门到精通 - 神经网络入门:从感知机到反向传播数学揭秘

机器学习从入门到精通 - 神经网络入门&#xff1a;从感知机到反向传播数学揭秘开场白&#xff1a;点燃你的好奇心 各位&#xff0c;有没有觉得那些能识图、懂人话、下棋碾压人类的AI特别酷&#xff1f;它们的"大脑"核心&#xff0c;很多时候就是神经网络&#xff01;…

神经网络模型介绍

如果你用过人脸识别解锁手机、刷到过精准推送的短视频&#xff0c;或是体验过 AI 聊天机器人&#xff0c;那么你已经在和神经网络打交道了。作为深度学习的核心技术&#xff0c;神经网络模仿人脑的信息处理方式&#xff0c;让机器拥有了 “学习” 的能力。一、什么是神经网络&a…

苹果开发中什么是Storyboard?object-c 和swiftui 以及Storyboard到底有什么关系以及逻辑?优雅草卓伊凡

苹果开发中什么是Storyboard&#xff1f;object-c 和swiftui 以及Storyboard到底有什么关系以及逻辑&#xff1f;优雅草卓伊凡引言由于最近有个客户咨询关于 苹果内购 in-purchase 的问题做了付费咨询处理&#xff0c;得到问题&#xff1a;“昨天试着把您的那几部分code 组装成…