JVM 内存区域划分与垃圾回收机制详解
引言
Java虚拟机(JVM)是Java程序运行的核心环境,其内存管理和垃圾回收机制直接影响着应用程序的性能和稳定性。深入理解JVM内存区域划分和垃圾回收机制,对于编写高性能Java程序、进行内存调优、解决内存泄漏问题具有重要意义。
JVM内存区域划分
程序计数器(Program Counter Register)
程序计数器是JVM内存中最小的内存区域,用于记录当前线程执行的字节码指令地址。
// 程序计数器相关概念演示
public class ProgramCounterExample {
// 模拟字节码执行过程
public static void executeBytecode() {
int a = 1; // 程序计数器指向这行指令
int b = 2; // 程序计数器递增,指向下一行
int c = a + b; // 程序计数器继续递增
System.out.println(c);
}
// 线程切换时程序计数器的保存与恢复
public static void threadSwitching() {
Thread thread1 = new Thread(() -> {
// 每个线程都有独立的程序计数器
for (int i = 0; i < 100; i++) {
System.out.println("Thread 1: " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
Thread thread2 = new Thread(() -> {
// 每个线程的程序计数器独立记录执行位置
for (int i = 0; i < 100; i++) {
System.out.println("Thread 2: " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
thread1.start();
thread2.start();
}
// 程序计数器的唯一无内存溢出区域
public static void noOutOfMemory() {
// 程序计数器不会发生OutOfMemoryError
// 因为它只存储当前线程执行的字节码地址
// 每个线程的程序计数器大小固定
}
}
虚拟机栈(Java Virtual Machine Stacks)
虚拟机栈描述的是Java方法执行的内存模型,每个方法执行时都会创建一个栈帧。
// 虚拟机栈深度测试
public class VirtualMachineStackExample {
private static int stackDepth = 0;
// 递归调用测试栈深度
public static void recursiveCall() {
stackDepth++;
try {
recursiveCall();
} catch (StackOverflowError e) {
System.out.println("Maximum stack depth: " + stackDepth);
System.out.println("Stack overflow occurred: " + e.getMessage());
throw e;
}
}
// 栈帧结构演示
public static int calculateSum(int n) {
if (n <= 1) {
return 1; // 栈帧1:返回1
}
int result = n + calculateSum(n - 1); // 栈帧2,3,4...
return result; // 每个栈帧返回计算结果
}
// 局部变量表演示
public static void localVarTable() {
int localVar1 = 10; // 局部变量表slot 0
String localVar2 = "test"; // 局部变量表slot 1
double localVar3 = 3.14; // 局部变量表slot 2,3 (double占2个slot)
// 每个方法都有独立的局部变量表
// 存储方法参数、局部变量等
}
// 操作数栈演示
public static int operationStackDemo() {
int a = 10; // 加载到操作数栈
int b = 20; // 加载到操作数栈
int c = a + b; // 从操作数栈弹出a,b,计算结果压入栈
return c; // 返回值压入操作数栈
}
// 动态链接演示
public static void dynamicLinking() {
// 每个栈帧都包含一个指向运行时常量池的引用
// 用于支持方法调用过程中的动态链接
String str = "Hello"; // 常量池引用
System.out.println(str); // 动态链接到String类的toString方法
}
// 方法返回地址
public static int methodReturnAddress() {
int result = calculateSum(5); // 调用方法
// 方法执行完毕后,返回到调用处的下一条指令
return result;
}
}
本地方法栈(Native Method Stacks)
本地方法栈为虚拟机使用到的Native方法服务。
// 本地方法栈相关概念
public class NativeMethodStackExample {
// 声明本地方法
public native void nativeMethod();
// 本地方法调用示例
public void callNativeMethod() {
// 调用本地方法时,JVM会在本地方法栈中创建栈帧
nativeMethod();
}
// 本地方法栈溢出示例(理论演示)
public static void nativeStackOverflow() {
// 在实际使用中,本地方法栈也可能出现栈溢出
// 但这种情况相对较少见
}
static {
// 加载本地库
System.loadLibrary("nativeLibrary");
}
}
Java堆(Java Heap)
Java堆是JVM管理的内存中最大的一块,所有线程共享,用于存放对象实例。
// Java堆内存管理演示
public class JavaHeapExample {
// 对象分配演示
public static void objectAllocation() {
// 对象在堆中分配
String str = new String("Hello World"); // 字符串对象在堆中
User user = new User("Alice", 25); // User对象在堆中
int[] array = new int[1000]; // 数组对象在堆中
}
// 堆内存溢出示例
public static void heapOutOfMemory() {
List<byte[]> list = new ArrayList<>();
int count = 0;
try {
while (true) {
// 持续分配大对象,直到堆内存不足
list.add(new byte[1024 * 1024]); // 每次分配1MB
count++;
if (count % 100 == 0) {
System.out.println("Allocated " + count + " MB");
}
}
} catch (OutOfMemoryError e) {
System.out.println("Heap OutOfMemoryError occurred: " + e.getMessage());
System.out.println("Allocated approximately: " + count + " MB");
}
}
// 堆内存区域划分
public static class HeapRegionDemo {
// 新生代(Young Generation)
// - Eden区:新对象分配区域
// - Survivor区:Survivor From和Survivor To
// 老年代(Old Generation)
// - 存放经过多次GC后仍然存活的对象
// 永久代/元空间(Permanent Generation/Metaspace)
// - 存放类信息、常量、静态变量等
public static void generationalDemo() {
// 大多数对象在Eden区分配
Object obj1 = new Object(); // 分配在Eden区
// 经过一次Minor GC后,存活对象进入Survivor区
// 经过多次GC后,进入老年代
}
}
// 大对象直接进入老年代
public static void largeObjectAllocation() {
// 大对象(如大数组)可能直接分配到老年代
// 以避免在新生代之间频繁复制
byte[] largeArray = new byte[2 * 1024 * 1024]; // 2MB大数组
}
// 对象年龄计数器
public static void objectAgeCounter() {
// 每次Minor GC后,对象年龄+1
// 当年龄达到阈值(默认15)时,进入老年代
Object obj = new Object();
// 经过15次GC后,obj可能进入老年代
}
}
// 示例用户类
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name; }
public int getAge() {
return age; }
}
方法区(Method Area)
方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
// 方法区相关概念演示
public class MethodAreaExample {
// 静态变量存储在方法区
private static String staticVar = "Static Variable";
private static final String CONSTANT = "Constant Value";
// 类信息存储在方法区
public static class InnerClass {
private String innerField;
public InnerClass(String innerField) {
this.innerField = innerField;
}
}
// 方法区溢出示例
public static void methodAreaOutOfMemory() {
// 在JDK 8之前,方法区是永久代,容易出现内存溢出
// 在JDK 8及以后,方法区被元空间替代
// 动态生成大量类来测试方法区溢出(需要特殊工具)
// 例如使用CGLIB动态生成类
}
// 运行时常量池演示
public static void runtimeConstantPool() {
String str1 = "Hello";
String str2 = "Hello";
// str1和str2指向常量池中的同一个对象
String str3 = new String("Hello");
// str3指向堆中的新对象
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
}
// 字符串常量池
public static void stringConstantPool() {
// 字符串常量池是方法区的一部分
String s1 = "test";
String s2 = "test";
String s3 = new String("test");
String s4 = s3.intern();
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1 == s4); // true
}
}
垃圾回收算法
标记-清除算法(Mark-Sweep)
标记-清除算法是最基础的垃圾收集算法,分为标记和清除两个阶段。
// 标记-清除算法实现
public class MarkSweepGC {
// 模拟对象标记过程
static class MockObject {
private boolean marked = false;
private MockObject next;
public MockObject(MockObject next) {
this.next = next;
}
public boolean isMarked() {
return marked; }
public void mark() {
this.marked = true; }
public void unmark() {
this.marked = false; }
public MockObject getNext() {
return next; }
}
private MockObject root; // GC Root
// 标记阶段
public void markPhase() {
if (root != null) {
markObject(root);
}
}
private void markObject(MockObject obj) {
if (obj != null && !obj.isMarked()) {
obj.mark();
// 递归标记引用的对象
markObject(obj.getNext());
}
}
// 清除阶段
public void sweepPhase() {
// 遍历堆中的所有对象,清除未标记的对象
// 实际JVM中会有更复杂的实现
System.out.println("Sweeping unmarked objects...");
}
// 算法特点分析
public void algorithmCharacteristics() {
System.out.println("标记-清除算法特点:");
System.out.println("1. 简单易实现");
System.out.println("2. 效率不高(需要遍历所有对象)");
System.out.println("3. 会产生内存碎片");
System.out.println("4. 标记和清除过程都需要暂停程序");
}
}
复制算法(Copying)
复制算法将内存按容量划分为大小相等的两块,每次只使用其中一块。
// 复制算法实现
public class CopyingGC {
static class MockObject {
private int id;
private MockObject next;
private boolean copied = false;
public MockObject(int id, MockObject next) {
this.id = id;
this.next = next;
}
public int getId() {
return id; }
public MockObject getNext() {
return next; }
public boolean isCopied() {
return copied; }
public void setCopied(boolean copied) {
this.copied = copied; }
}
// 模拟Eden区和Survivor区
private MockObject[] fromSpace = new MockObject[1000];
private MockObject[] toSpace = new MockObject[1000];
private int fromIndex = 0;
private int toIndex = 0;
// 复制过程
public void copyLiveObjects() {
// 从fromSpace复制存活对象到toSpace
for (int i = 0; i < fromIndex; i++) {
MockObject obj = fromSpace[i];
if (obj != null && isLiveObject(obj)) {
// 假设isLiveObject判断对象是否存活
toSpace[toIndex++] = obj;
obj.setCopied(true);
}
}
// 交换fromSpace和toSpace
MockObject[] temp = fromSpace;
fromSpace = toSpace;
toSpace = temp;
fromIndex = toIndex;
toIndex = 0;
System.out.println("Copying completed, fromIndex: " + fromIndex);
}
// 判断对象是否存活(简化实现)
private boolean isLiveObject(MockObject obj) {
// 在实际GC中,会通过GC Root可达性分析判断
return obj.getId() % 2 == 0; // 假设偶数ID的对象存活
}
// 算法特点分析
public void algorithmCharacteristics() {
System.out.println("复制算法特点:");
System.out.println("1. 实现简单,运行高效");
System.out.println("2. 不会产生内存碎片");
System.out.println("3. 需要浪费一半内存空间");
System.out.println("4. 适合新生代(存活对象少)");
}
// 对象分配
public void allocateObject(int id) {
if (fromIndex < fromSpace.length) {
fromSpace[fromIndex++] = new MockObject(id, null);
} else {
System.out.println("Eden space full, triggering GC");
copyLiveObjects();
}
}
}
标记-整理算法(Mark-Compact)
标记-整理算法结合了标记-清除和复制算法的优点。
// 标记-整理算法实现
public class MarkCompactGC {
static class MockObject {
private int id;
private MockObject next;
private boolean marked = false;
private int newAddress = -1;
public MockObject(int id, MockObject next) {
this.id = id;
this.next = next;
}
public int getId() {
return id; }
public MockObject getNext() {
return next; }
public boolean isMarked() {
return marked; }
public void mark() {
this.marked = true; }
public int getNewAddress() {
return newAddress; }
public void setNewAddress(int address) {
this.newAddress = address; }
}
private MockObject[] heap = new MockObject[1000];
private int heapSize = 0;
// 标记阶段
public void markPhase() {
// 从GC Roots开始标记可达对象
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null) {
markReachable(heap[i]);
}
}
}
private void markReachable(MockObject obj) {
if (obj != null && !obj.isMarked()) {
obj.mark();
// 标记引用的对象
markReachable(obj.getNext());
}
}
// 整理阶段
public void compactPhase() {
int toIndex = 0;
// 将存活对象移动到内存的一端
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && heap[i].isMarked()) {
if (toIndex != i) {
heap[toIndex] = heap[i];
heap[i] = null; // 清除原位置
}
heap[toIndex].setNewAddress(toIndex);
toIndex++;
} else if (heap[i] != null) {
heap[i] = null; // 清除死亡对象
}
}
heapSize = toIndex;
System.out.println("Compaction completed, heapSize: " + heapSize);
}
// 算法特点分析
public void algorithmCharacteristics() {
System.out.println("标记-整理算法特点:");
System.out.println("1. 不会产生内存碎片");
System.out.println("2. 不需要额外的内存空间");
System.out.println("3. 移动对象需要更新引用");
System.out.println("4. 适合老年代(存活对象多)");
}
// 对象分配
public void allocateObject(int id) {
if (heapSize < heap.length) {
heap[heapSize++] = new MockObject(id, null);
}
}
}
分代收集算法(Generational Collection)
分代收集算法基于对象的存活周期将内存划分为几块,不同区域采用不同的收集算法。
// 分代收集算法实现
public class GenerationalGC {
static class MockObject {
private int id;
private int age = 0;
private MockObject next;
private boolean marked = false;
public MockObject(int id, MockObject next) {
this.id = id;
this.next = next;
}
public int getId() {
return id; }
public int getAge() {
return age; }
public void incrementAge() {
this.age++; }
public MockObject getNext() {
return next; }
public boolean isMarked() {
return marked; }
public void mark() {
this.marked = true; }
}
// 新生代:Eden + 2个Survivor
private MockObject[] eden = new MockObject[100];
private MockObject[] survivorFrom = new MockObject[50];
private MockObject[] survivorTo = new MockObject[50];
// 老年代
private MockObject[] oldGen = new MockObject[1000];
private int edenIndex = 0;
private int survivorFromIndex = 0;
private int survivorToIndex = 0;
private int oldGenIndex = 0;
// Minor GC:新生代收集
public void minorGC() {
System.out.println("Starting Minor GC...");
// 1. 标记新生代中的存活对象
markYoungGeneration();
// 2. 复制存活对象到Survivor区或晋升到老年代
int toPromoteCount = 0;
survivorToIndex = 0;
// 从Eden复制存活对象
for (int i = 0; i < edenIndex; i++) {
if (eden[i] != null && eden[i].isMarked()) {
if (eden[i].getAge() >= 15) {
// 年龄达到阈值,晋升到老年代
oldGen[oldGenIndex++] = eden[i];
toPromoteCount++;
} else {
// 复制到Survivor To区
survivorTo[survivorToIndex++] = eden[i];
eden[i].incrementAge();
}
}
eden[i] = null; // 清空Eden
}
// 从Survivor From复制存活对象
for (int i = 0; i < survivorFromIndex; i++) {
if (survivorFrom[i] != null && survivorFrom[i].isMarked()) {
if (survivorFrom[i].getAge() >= 15) {
oldGen[oldGenIndex++] = survivorFrom[i];
toPromoteCount++;
} else {
survivorTo[survivorToIndex++] = survivorFrom[i];
survivorFrom[i].incrementAge();
}
}
survivorFrom[i] = null; // 清空Survivor From
}
// 交换Survivor区
MockObject[] temp = survivorFrom;
survivorFrom = survivorTo;
survivorTo = temp;
survivorFromIndex = survivorToIndex;
survivorToIndex = 0;
System.out.println("Minor GC completed. Promoted " + toPromoteCount + " objects to old gen.");
}
// Major GC:老年代收集
public void majorGC() {
System.out.println("Starting Major GC...");
// 老年代使用标记-整理算法
markOldGeneration();
compactOldGeneration();
System.out.println("Major GC completed.");
}
// 标记新生代
private void markYoungGeneration() {
// 从GC Roots开始标记
// 这里简化为标记所有对象(实际会更复杂)
for (int i = 0; i < edenIndex; i++) {
if (eden[i] != null) {
eden[i].mark();
}
}
for (int i = 0; i < survivorFromIndex; i++) {
if (survivorFrom[i] != null) {
survivorFrom[i].mark();
}
}
}
// 标记老年代
private void markOldGeneration() {
for (int i = 0; i < oldGenIndex; i++) {
if (oldGen[i] != null) {
oldGen[i].mark();
}
}
}
// 整理老年代
private void compactOldGeneration() {
int toIndex = 0;
for (int i = 0; i < oldGenIndex; i++) {
if (oldGen[i] != null && oldGen[i].isMarked()) {
if (toIndex != i) {
oldGen[toIndex] = oldGen[i];
oldGen[i] = null;
}
toIndex++;
} else if (oldGen[i] != null) {
oldGen[i] = null;
}
}
oldGenIndex = toIndex;
}
// 对象分配
public void allocateObject(int id) {
if (edenIndex < eden.length) {
eden[edenIndex++] = new MockObject(id, null);
} else {
System.out.println("Eden full, triggering Minor GC");
minorGC();
// 重新尝试分配
if (edenIndex < eden.length) {
eden[edenIndex++] = new MockObject(id, null);
} else {
System.out.println("Still no space after GC, promoting to old gen");
oldGen[oldGenIndex++] = new MockObject(id, null);
}
}
}
// 模拟对象年龄增长
public void simulateAging() {
for (int i = 0; i < survivorFromIndex; i++) {
if (survivorFrom[i] != null) {
survivorFrom[i].incrementAge();
}
}
}
}
垃圾收集器
Serial收集器
Serial收集器是最基本的垃圾收集器,使用单线程进行垃圾收集。
// Serial收集器概念演示
public class SerialGCConcept {
// 模拟单线程GC过程
public static class MockSerialGC {
private MockObject[] heap = new MockObject[1000];
private int heapSize = 0;
public void collect() {
System.out.println("Serial GC started - STW (Stop The World)");
// 1. 标记阶段(单线程)
long startTime = System.currentTimeMillis();
markLiveObjects();
long markTime = System.currentTimeMillis() - startTime;
// 2. 清除阶段(单线程)
startTime = System.currentTimeMillis();
sweepDeadObjects();
long sweepTime = System.currentTimeMillis() - startTime;
System.out.println("Serial GC completed. Mark time: " + markTime + "ms, Sweep time: " + sweepTime + "ms");
System.out.println("STW ended");
}
private void markLiveObjects() {
// 从GC Roots开始标记
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && isLive(heap[i])) {
heap[i].mark();
}
}
}
private void sweepDeadObjects() {
int newHeapSize = 0;
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && heap[i].isMarked()) {
heap[newHeapSize++] = heap[i];
heap[i].unmark(); // 清除标记
} else if (heap[i] != null) {
heap[i] = null; // 回收死亡对象
}
}
heapSize = newHeapSize;
}
private boolean isLive(MockObject obj) {
// 简化的存活判断
return obj.getId() % 3 != 0; // 假设id不是3的倍数的对象存活
}
}
// Serial收集器特点
public void characteristics() {
System.out.println("Serial收集器特点:");
System.out.println("1. 单线程收集");
System.out.println("2. 简单高效");
System.out.println("3. 适用于客户端模式");
System.out.println("4. 新生代采用复制算法");
System.out.println("5. 老年代采用标记-整理算法");
}
}
ParNew收集器
ParNew收集器是Serial收集器的多线程版本。
// ParNew收集器概念演示
public class ParNewGCConcept {
public static class MockParNewGC {
private MockObject[] heap = new MockObject[1000];
private int heapSize = 0;
private int threadCount = 4;
public void collect() {
System.out.println("ParNew GC started - STW");
// 分区并行处理
int objectsPerThread = heapSize / threadCount;
List<CompletableFuture<Void>> futures = new ArrayList<>();
long startTime = System.currentTimeMillis();
// 并行标记
for (int i = 0; i < threadCount; i++) {
final int start = i * objectsPerThread;
final int end = (i == threadCount - 1) ? heapSize : (i + 1) * objectsPerThread;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
for (int j = start; j < end; j++) {
if (heap[j] != null && isLive(heap[j])) {
heap[j].mark();
}
}
});
futures.add(future);
}
// 等待所有线程完成标记
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
long markTime = System.currentTimeMillis() - startTime;
// 串行清理(为简单起见,实际可能并行)
startTime = System.currentTimeMillis();
sweepDeadObjects();
long sweepTime = System.currentTimeMillis() - startTime;
System.out.println("ParNew GC completed. Mark time: " + markTime + "ms, Sweep time: " + sweepTime + "ms");
}
private void sweepDeadObjects() {
int newHeapSize = 0;
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && heap[i].isMarked()) {
heap[newHeapSize++] = heap[i];
heap[i].unmark();
} else if (heap[i] != null) {
heap[i] = null;
}
}
heapSize = newHeapSize;
}
private boolean isLive(MockObject obj) {
return obj.getId() % 3 != 0;
}
}
// ParNew收集器特点
public void characteristics() {
System.out.println("ParNew收集器特点:");
System.out.println("1. 多线程并行收集");
System.out.println("2. 仅作用于新生代");
System.out.println("3. 配合CMS收集器使用");
System.out.println("4. 默认线程数等于CPU核心数");
}
}
Parallel Scavenge收集器
Parallel Scavenge收集器关注吞吐量。
// Parallel Scavenge收集器概念演示
public class ParallelScavengeGCConcept {
public static class MockParallelScavengeGC {
private MockObject[] heap = new MockObject[1000];
private int heapSize = 0;
private int threadCount = Runtime.getRuntime().availableProcessors();
// 关注吞吐量的参数
private double targetGCTimeRatio = 0.01; // GC时间占比目标
private int maxGCPauseMillis = 200; // 最大暂停时间目标
public void collect() {
System.out.println("Parallel Scavenge GC started");
long startTime = System.currentTimeMillis();
// 并行标记
parallelMark();
// 并行复制
parallelCopy();
long gcTime = System.currentTimeMillis() - startTime;
long totalTime = getRunningTime();
double gcRatio = (double) gcTime / totalTime;
System.out.println("GC ratio: " + gcRatio + ", Target: " + targetGCTimeRatio);
// 根据GC时间比例调整参数
if (gcRatio > targetGCTimeRatio) {
adjustParametersForThroughput();
}
}
private void parallelMark() {
int objectsPerThread = heapSize / threadCount;
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
final int start = i * objectsPerThread;
final int end = (i == threadCount - 1) ? heapSize : (i + 1) * objectsPerThread;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
for (int j = start; j < end; j++) {
if (heap[j] != null && isLive(heap[j])) {
heap[j].mark();
}
}
});
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
private void parallelCopy() {
// 并行复制存活对象
List<MockObject> liveObjects = new ArrayList<>();
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && heap[i].isMarked()) {
liveObjects.add(heap[i]);
heap[i].unmark();
} else if (heap[i] != null) {
heap[i] = null;
}
}
// 重新分配
heapSize = 0;
for (MockObject obj : liveObjects) {
heap[heapSize++] = obj;
}
}
private long getRunningTime() {
// 模拟获取程序运行总时间
return System.currentTimeMillis();
}
private void adjustParametersForThroughput() {
// 根据吞吐量目标调整GC参数
System.out.println("Adjusting parameters for better throughput");
}
private boolean isLive(MockObject obj) {
return obj.getId() % 2 == 0; // 假设偶数ID的对象存活
}
}
// Parallel Scavenge收集器特点
public void characteristics() {
System.out.println("Parallel Scavenge收集器特点:");
System.out.println("1. 关注吞吐量");
System.out.println("2. 自适应调节策略");
System.out.println("3. 新生代并行收集");
System.out.println("4. 适合后台计算服务");
}
}
CMS收集器
CMS(Concurrent Mark Sweep)收集器关注最短回收停顿时间。
// CMS收集器概念演示
public class CMSGCConcept {
public static class MockCMSGC {
private MockObject[] heap = new MockObject[1000];
private int heapSize = 0;
private volatile boolean concurrentMarking = false;
public void collect() {
System.out.println("CMS GC started");
// 1. 初始标记(STW)
long startTime = System.currentTimeMillis();
initialMark();
long initialMarkTime = System.currentTimeMillis() - startTime;
System.out.println("Initial mark completed - STW: " + initialMarkTime + "ms");
// 2. 并发标记(与用户线程并发)
startTime = System.currentTimeMillis();
concurrentMark();
long concurrentMarkTime = System.currentTimeMillis() - startTime;
System.out.println("Concurrent mark completed: " + concurrentMarkTime + "ms");
// 3. 重新标记(STW)
startTime = System.currentTimeMillis();
remark();
long remarkTime = System.currentTimeMillis() - startTime;
System.out.println("Remark completed - STW: " + remarkTime + "ms");
// 4. 并发清除(与用户线程并发)
startTime = System.currentTimeMillis();
concurrentSweep();
long concurrentSweepTime = System.currentTimeMillis() - startTime;
System.out.println("Concurrent sweep completed: " + concurrentSweepTime + "ms");
}
private void initialMark() {
// 仅标记GC Roots直接关联的对象
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && isGcRoot(heap[i])) {
heap[i].mark();
}
}
}
private void concurrentMark() {
concurrentMarking = true;
// 启动并发标记线程
Thread markThread = new Thread(() -> {
for (int i = 0; i < heapSize && concurrentMarking; i++) {
if (heap[i] != null && !heap[i].isMarked()) {
markReachable(heap[i]);
}
}
});
markThread.start();
// 模拟用户线程继续运行
simulateUserThreads();
try {
markThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
concurrentMarking = false;
}
private void remark() {
// 修正并发标记期间对象引用关系变化
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && hasReferenceChanged(heap[i])) {
markReachable(heap[i]);
}
}
}
private void concurrentSweep() {
// 并发清除死亡对象
Thread sweepThread = new Thread(() -> {
for (int i = 0; i < heapSize; i++) {
if (heap[i] != null && !heap[i].isMarked()) {
heap[i] = null; // 清除死亡对象
} else if (heap[i] != null) {
heap[i].unmark(); // 清除标记
}
}
});
sweepThread.start();
// 用户线程继续运行
simulateUserThreads();
try {
sweepThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void markReachable(MockObject obj) {
if (obj != null && !obj.isMarked()) {
obj.mark();
// 标记引用的对象
if (obj.getNext() != null) {
markReachable(obj.getNext());
}
}
}
private boolean isGcRoot(MockObject obj) {
// 简化的GC Root判断
return obj.getId() < 10; // 假设ID小于10的是GC Root
}
private boolean hasReferenceChanged(MockObject obj) {
// 模拟并发标记期间引用关系变化
return Math.random() > 0.9; // 10%概率引用关系变化
}
private void simulateUserThreads() {
// 模拟用户线程在GC期间继续运行
try {
Thread.sleep(100); // 模拟用户线程工作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// CMS收集器特点
public void characteristics() {
System.out.println("CMS收集器特点:");
System.out.println("1. 关注最短停顿时间");
System.out.println("2. 基于标记-清除算法");
System.out.println("3. 并发收集,低停顿");
System.out.println("4. 会产生内存碎片");
System.out.println("5. 对CPU资源敏感");
}
}
G1收集器
G1(Garbage-First)收集器面向服务端应用。
// G1收集器概念演示
public class G1GCConcept {
// G1将堆划分为多个Region
public static class Region {
private MockObject[] objects = new MockObject[100];
private int objectCount = 0;
private boolean isHumongous = false; // 是否是大对象Region
private boolean isOld = false; // 是否是老年代Region
public void addObject(MockObject obj) {
if (objectCount < objects.length) {
objects[objectCount++] = obj;
}
}
public int getLiveObjectCount() {
int count = 0;
for (int i = 0; i < objectCount; i++) {
if (objects[i] != null && objects[i].isMarked()) {
count++;
}
}
return count;
}
public boolean isEmpty() {
return objectCount == 0;
}
}
public static class MockG1GC {
private Region[] regions = new Region[200]; // 200个Region
private PriorityQueue<Region> collectionSet = new PriorityQueue<>((r1, r2) ->
Integer.compare(r2.getLiveObjectCount(), r1.getLiveObjectCount())); // 按垃圾量排序
public MockG1GC() {
for (int i = 0; i < regions.length; i++) {
regions[i] = new Region();
}
}
public void collect() {
System.out.println("G1 GC started");
// 1. 全局并发标记
concurrentMarking();
// 2. 选择回收价值最大的Region集合
selectCollectionSet();
// 3. 并行回收选定的Region
parallelEvacuation();
}
private void concurrentMarking() {
System.out.println("Concurrent marking phase");
// 并发标记整个堆
for (Region region : regions) {
markRegion(region);
}
}
private void markRegion(Region region) {
// 标记Region中的存活对象
for (int i = 0; i < region.objectCount; i++) {
if (region.objects[i] != null && isLive(region.objects[i])) {
region.objects[i].mark();
}
}
}
private void selectCollectionSet() {
// 选择垃圾最多的Region进行回收
collectionSet.clear();
for (Region region : regions) {
if (!region.isEmpty() && region.getLiveObjectCount() < region.objectCount * 0.8) {
collectionSet.offer(region);
}
}
}
private void parallelEvacuation() {
System.out.println("Parallel evacuation phase");
// 并行回收选中的Region
while (!collectionSet.isEmpty()) {
Region region = collectionSet.poll();
evacuateRegion(region);
}
}
private void evacuateRegion(Region region) {
// 将Region中的存活对象复制到其他Region
List<MockObject> liveObjects = new ArrayList<>();
for (int i = 0; i < region.objectCount; i++) {
if (region.objects[i] != null && region.objects[i].isMarked()) {
liveObjects.add(region.objects[i]);
region.objects[i].unmark();
}
}
// 清空原Region
region.objectCount = 0;
// 将存活对象分配到新的Region
allocateLiveObjects(liveObjects);
}
private void allocateLiveObjects(List<MockObject> liveObjects) {
// 将存活对象分配到合适的Region
for (MockObject obj : liveObjects) {
// 简化的分配逻辑
for (Region region : regions) {
if (region.objectCount < region.objects.length) {
region.addObject(obj);
break;
}
}
}
}
private boolean isLive(MockObject obj) {
return obj.getId() % 3 != 0;
}
}
// G1收集器特点
public void characteristics() {
System.out.println("G1收集器特点:");
System.out.println("1. 面向服务端应用");
System.out.println("2. 并行与并发");
System.out.println("3. 分代收集");
System.out.println("4. 空间整合(类似标记-整理)");
System.out.println("5. 可预测的停顿");
}
}
ZGC收集器
ZGC是JDK 11引入的低延迟垃圾收集器。
// ZGC概念演示(简化版)
public class ZGCConcept {
// ZGC的内存布局概念
public static class ZGCHeap {
// ZGC使用Colored Pointers技术
private static final int MAPPABLE = 0x01; // 可映射
private static final int REMAPPED = 0x02; // 已重映射
private static final int MARKED0 = 0x04; // 标记0
private static final int MARKED1 = 0x08; // 标记1
// 并发标记阶段
public void concurrentMark() {
System.out.println("ZGC Concurrent Mark Phase");
// 使用Colored Pointers进行并发标记
// 不需要STW,用户线程可以继续运行
}
// 并发重映射阶段
public void concurrentRemap() {
System.out.println("ZGC Concurrent Remap Phase");
// 重映射阶段,修正指针
}
// 并发转移阶段
public void concurrentRelocate() {
System.out.println("ZGC Concurrent Relocate Phase");
// 并发转移存活对象到新区域
}
}
// ZGC特点
public void characteristics() {
System.out.println("ZGC收集器特点:");
System.out.println("1. 超低延迟(<10ms)");
System.out.println("2. 并发执行,几乎无停顿");
System.out.println("3. 支持大堆内存(TB级别)");
System.out.println("4. 使用Colored Pointers技术");
System.out.println("5. Region-based内存管理");
}
}
内存调优与监控
内存参数配置
JVM提供了丰富的参数来配置内存区域大小和垃圾收集器。
// 内存参数配置示例
public class MemoryTuningExample {
// 常用JVM参数说明
public void commonJVMParameters() {
System.out.println("常用JVM内存参数:");
System.out.println("-Xms<size> 初始堆大小");
System.out.println("-Xmx<size> 最大堆大小");
System.out.println("-Xmn<size> 新生代大小");
System.out.println("-XX:NewRatio=<ratio> 新生代与老年代比例");
System.out.println("-XX:SurvivorRatio=<ratio> Eden区与Survivor区比例");
System.out.println("-XX:MaxTenuringThreshold=<n> 最大年龄阈值");
System.out.println("-XX:+UseSerialGC 使用Serial收集器");
System.out.println("-XX:+UseParNewGC 使用ParNew收集器");
System.out.println("-XX:+UseParallelGC 使用Parallel收集器");
System.out.println("-XX:+UseConcMarkSweepGC 使用CMS收集器");
System.out.println("-XX:+UseG1GC 使用G1收集器");
}
// 内存配置策略
public void memoryConfigurationStrategy() {
System.out.println("内存配置策略:");
System.out.println("1. 根据应用特点选择合适的收集器");
System.out.println("2. 合理设置堆大小,避免频繁GC");
System.out.println("3. 调整新生代大小,平衡Minor GC频率");
System.out.println("4. 监控GC日志,分析性能瓶颈");
}
// GC日志分析
public void gcLogAnalysis() {
System.out.println("GC日志分析要点:");
System.out.println("1. GC频率:Minor GC和Major GC的频率");
System.out.println("2. GC时间:每次GC的停顿时间");
System.out.println("3. 内存使用:各代内存的使用情况");
System.out.println("4. 晋升速率:对象从新生代到老年代的速率");
}
}
内存监控工具
JVM提供了多种工具来监控内存使用情况。
// 内存监控示例
public class MemoryMonitoring {
// 使用ManagementFactory监控内存
public void monitorMemoryUsage() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("Heap Memory Usage:");
System.out.println(" Used: " + heapUsage.getUsed() / (1024 * 1024) + " MB");
System.out.println(" Max: " + heapUsage.getMax() / (1024 * 1024) + " MB");
System.out.println(" Committed: " + heapUsage.getCommitted() / (1024 * 1024) + " MB");
System.out.println("Non-Heap Memory Usage:");
System.out.println(" Used: " + nonHeapUsage.getUsed() / (1024 * 1024) + " MB");
System.out.println(" Max: " + nonHeapUsage.getMax() / (1024 * 1024) + " MB");
System.out.println(" Committed: " + nonHeapUsage.getCommitted() / (1024 * 1024) + " MB");
}
// 监控垃圾收集器信息
public void monitorGarbageCollectors() {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC Name: " + gcBean.getName());
System.out.println(" Collection Count: " + gcBean.getCollectionCount());
System.out.println(" Collection Time: " + gcBean.getCollectionTime() + " ms");
String[] memoryPoolNames = gcBean.getMemoryPoolNames();
System.out.println(" Associated Memory Pools: " + String.join(", ", memoryPoolNames));
}
}
// 监控内存池
public void monitorMemoryPools() {
List<MemoryPoolMXBean> memoryPoolBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean poolBean : memoryPoolBeans) {
System.out.println("Memory Pool: " + poolBean.getName());
System.out.println(" Type: " + poolBean.getType());
System.out.println(" Usage: " + poolBean.getUsage());
if (poolBean.getPeakUsage() != null) {
System.out.println(" Peak Usage: " + poolBean.getPeakUsage());
}
}
}
// 模拟内存使用监控
public void simulateMemoryMonitoring() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
monitorMemoryUsage();
System.out.println("---");
}, 0, 5, TimeUnit.SECONDS);
// 模拟内存分配
List<byte[]> memoryHog = new ArrayList<>();
ScheduledExecutorService allocationScheduler = Executors.newScheduledThreadPool(1);
allocationScheduler.scheduleAtFixedRate(() -> {
if (memoryHog.size() < 100) {
memoryHog.add(new byte[1024 * 1024]); // 每次分配1MB
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
}
常见内存问题诊断
了解常见的内存问题及其诊断方法。
// 内存问题诊断示例
public class MemoryProblemDiagnosis {
// 内存泄漏示例
public static class MemoryLeakExample {
private List<String> cache = new ArrayList<>();
public void addToCache(String data) {
// 没有清理机制,可能导致内存泄漏
cache.add(data);
}
// 正确的清理方法
public void clearCache() {
cache.clear();
}
// 使用WeakReference避免内存泄漏
private Map<String, WeakReference<Object>> weakCache = new HashMap<>();
public void addToWeakCache(String key, Object value) {
weakCache.put(key, new WeakReference<>(value));
}
}
// 内存溢出示例
public void demonstrateOutOfMemory() {
List<byte[]> list = new ArrayList<>();
try {
while (true) {
list.add(new byte[1024 * 1024]); // 每次分配1MB
}
} catch (OutOfMemoryError e) {
System.out.println("OutOfMemoryError caught: " + e.getMessage());
list.clear(); // 清理内存
}
}
// 栈溢出示例
public void demonstrateStackOverflow() {
try {
recursiveMethod();
} catch (StackOverflowError e) {
System.out.println("StackOverflowError caught: " + e.getMessage());
}
}
private int recursiveMethod() {
return recursiveMethod() + 1; // 无限递归导致栈溢出
}
// GC开销限制超出示例
public void demonstrateGCOverheadLimit() {
List<byte[]> list = new ArrayList<>();
try {
while (true) {
// 创建大量短生命周期对象
for (int i = 0; i < 1000; i++) {
list.add(new byte[1024]); // 1KB对象
}
// 立即丢弃大部分对象
list.subList(0, 900).clear();
}
} catch (OutOfMemoryError e) {
System.out.println("GC overhead limit exceeded: " + e.getMessage());
}
}
// 内存诊断工具使用
public void diagnosticTools() {
System.out.println("常用内存诊断工具:");
System.out.println("1. jstat - 监控JVM统计信息");
System.out.println("2. jmap - 生成堆转储快照");
System.out.println("3. jhat - 分析堆转储快照");
System.out.println("4. jconsole - 图形化监控工具");
System.out.println("5. VisualVM - 综合性能分析工具");
System.out.println("6. JProfiler - 商业性能分析工具");
System.out.println("7. YourKit - 商业性能分析工具");
}
}
性能调优实践
实际的性能调优案例和最佳实践。
// 性能调优实践示例
public class PerformanceTuningPractice {
// 对象池模式减少GC压力
public static class ObjectPool<T> {
private final Queue<T> pool = new ConcurrentLinkedQueue<>();
private final Supplier<T> factory;
private final Consumer<T> resetter;
public ObjectPool(Supplier<T> factory, Consumer<T> resetter) {
this.factory = factory;
this.resetter = resetter;
}
public T acquire() {
T object = pool.poll();
if (object == null) {
object = factory.get();
}
return object;
}
public void release(T object) {
resetter.accept(object);
pool.offer(object);
}
}
// 字符串池优化
public static class StringPoolOptimization {
private final Map<String, String> stringPool = new ConcurrentHashMap<>();
public String internString(String str) {
return stringPool.computeIfAbsent(str, s -> s);
}
// 使用String.intern()优化内存
public String useIntern(String str) {
return str.intern(); // 将字符串放入常量池
}
}
// 大对象处理策略
public static class LargeObjectHandling {
// 使用直接内存避免堆内存压力
public ByteBuffer allocateDirectMemory(int size) {
return ByteBuffer.allocateDirect(size); // 直接内存,不受堆大小限制
}
// 分块处理大对象
public void processLargeData(byte[] largeData) {
int chunkSize = 1024 * 1024; // 1MB chunks
for (int i = 0; i < largeData.length; i += chunkSize) {
int end = Math.min(i + chunkSize, largeData.length);
byte[] chunk = Arrays.copyOfRange(largeData, i, end);
processChunk(chunk);
}
}
private void processChunk(byte[] chunk) {
// 处理数据块
}
}
// 引用类型选择
public static class ReferenceTypeSelection {
private Map<String, Object> strongCache = new HashMap<>();
private Map<String, WeakReference<Object>> weakCache = new HashMap<>();
private Map<String, SoftReference<Object>> softCache = new HashMap<>();
// 强引用:对象不会被回收
public void useStrongReference(String key, Object value) {
strongCache.put(key, value);
}
// 软引用:内存不足时会被回收
public void useSoftReference(String key, Object value) {
softCache.put(key, new SoftReference<>(value));
}
// 弱引用:GC时会被回收
public void useWeakReference(String key, Object value) {
weakCache.put(key, new WeakReference<>(value));
}
}
// 性能调优检查清单
public void tuningChecklist() {
System.out.println("JVM性能调优检查清单:");
System.out.println("1. 分析GC日志,确定GC频率和停顿时间");
System.out.println("2. 检查内存使用情况,避免内存泄漏");
System.out.println("3. 选择合适的垃圾收集器");
System.out.println("4. 调整堆大小和各代比例");
System.out.println("5. 优化对象分配和生命周期管理");
System.out.println("6. 使用对象池减少GC压力");
System.out.println("7. 合理使用不同类型的引用");
System.out.println("8. 监控和调优JVM参数");
}
}
JVM内存区域划分和垃圾回收机制是Java程序性能优化的核心。通过深入理解各内存区域的特点、垃圾回收算法的原理和各种收集器的特性,开发者可以更好地进行内存调优,构建高性能的Java应用程序。
关于作者
🌟 我是suxiaoxiang,一位热爱技术的开发者
💡 专注于Java生态和前沿技术分享
🚀 持续输出高质量技术内容
如果这篇文章对你有帮助,请支持一下:
👍 点赞
⭐ 收藏
👀 关注
您的支持是我持续创作的动力!感谢每一位读者的关注与认可!