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 性能调优策略
调优步骤
-
建立基线
- 记录当前GC性能指标
- 确定性能目标(吞吐量 vs 延迟)
- 分析应用特征
-
选择合适的GC
- 根据应用需求选择GC算法
- 考虑内存大小和延迟要求
- 评估CPU资源
-
参数调优
- 堆大小调优
- 分代比例调优
- GC线程数调优
-
监控和验证
- 使用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 + " 完成对象创建");
}
}
练习步骤:
- 使用不同的GC参数运行程序
- 在VisualVM中观察GC行为
- 比较不同GC算法的性能差异
- 分析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"
本章总结
关键要点
-
垃圾回收基础
- 理解GC的目标和挑战
- 掌握主要GC算法原理
- 了解分代垃圾回收机制
-
VisualVM GC监控
- 使用Monitor标签页监控GC活动
- 安装和使用Visual GC插件
- 解读GC性能指标
-
GC日志分析
- 配置GC日志输出
- 分析GC日志内容
- 识别GC性能问题
-
GC调优策略
- 选择合适的垃圾回收器
- 调整JVM参数
- 监控和验证调优效果
最佳实践
-
监控策略
- 建立GC性能基线
- 持续监控GC指标
- 设置合理的告警阈值
-
调优原则
- 明确性能目标(吞吐量 vs 延迟)
- 渐进式调优,避免大幅度改动
- 充分测试验证调优效果
-
性能优化
- 减少对象创建和生命周期
- 合理设置堆大小
- 选择适合的GC算法
-
故障诊断
- 分析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 插件
- 打开 VisualVM
- 选择 Tools → Plugins
- 在 Available Plugins 中找到 Visual GC
- 点击 Install 安装插件
- 重启 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");