Java垃圾回收机制深度剖析
1. JVM内存结构
在深入垃圾回收之前,我们需要先了解JVM的内存结构:
- 堆(Heap):存储对象实例,是垃圾收集器管理的主要区域。
- 方法区(Method Area):存储类信息、常量、静态变量等。
- 虚拟机栈(VM Stack):存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 本地方法栈(Native Method Stack):为本地方法服务。
- 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器。
其中,堆和方法区是线程共享的,而虚拟机栈、本地方法栈和程序计数器是线程私有的。
2. 对象的创建与内存分配
当我们创建一个新对象时,JVM会执行以下步骤:
- 类加载检查
- 分配内存
- 初始化零值
- 设置对象头
- 执行方法
内存分配的两种方式:
- 指针碰撞:适用于内存规整的情况,通过移动指针分配内存。
- 空闲列表:适用于内存不规整的情况,维护一个列表记录可用内存块。
// 简化的内存分配过程
class MemoryAllocator {
private long currentMemoryPosition; // 假设使用指针碰撞
public Object allocate(int size) {
synchronized(this) { // 确保线程安全
long objectAddress = currentMemoryPosition;
currentMemoryPosition += size;
return new Object(objectAddress);
}
}
}
3. 对象的内存布局
在HotSpot虚拟机中,对象在内存中的布局可以分为三个部分:
- 对象头(Header)
- Mark Word:存储对象的哈希码、GC分代年龄、锁状态标志等。
- 类型指针:指向对象的类元数据。
- 实例数据(Instance Data)
- 对齐填充(Padding)
// 对象头的简化结构
class ObjectHeader {
long markWord; // 存储对象的运行时数据
long klassPointer; // 指向类元数据的指针
}
4. 垃圾回收算法的底层实现
4.1 标记-清除算法
标记阶段通常使用深度优先搜索(DFS)或广度优先搜索(BFS)来遍历对象图。
// 标记-清除算法的简化实现
class MarkSweepCollector {
Set<Object> liveObjects = new HashSet<>();
void mark(Object root) {
if (root == null || liveObjects.contains(root)) return;
liveObjects.add(root);
for (Object ref : getReferences(root)) {
mark(ref);
}
}
void sweep() {
for (Object obj : allObjects) {
if (!liveObjects.contains(obj)) {
free(obj);
}
}
}
}
4.2 复制算法
复制算法通常用于新生代垃圾回收,将内存分为Eden空间和两个Survivor空间。
// 复制算法的简化实现
class CopyCollector {
void collect(HeapRegion from, HeapRegion to) {
for (Object obj : from.getLiveObjects()) {
Object copy = copyObject(obj, to);
updateReferences(obj, copy);
}
from.clear();
}
}
4.3 标记-整理算法
标记-整理算法通常用于老年代垃圾回收,它需要移动对象并更新引用。
// 标记-整理算法的简化实现
class MarkCompactCollector {
void markAndCompact() {
mark();
compact();
updateReferences();
}
void compact() {
Object freePointer = heapStart;
for (Object obj : allObjects) {
if (isMarked(obj)) {
moveObject(obj, freePointer);
freePointer += obj.size();
}
}
}
}
5. 垃圾回收器的并发与并行
现代垃圾回收器通常采用并发或并行技术来提高效率:
- 并行(Parallel):多个线程同时执行垃圾回收,但在回收过程中需要暂停应用线程。
- 并发(Concurrent):垃圾回收线程与应用线程同时运行,减少停顿时间。
// 并行垃圾回收器的简化实现
class ParallelGC {
void parallelMark() {
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
threads.add(new Thread(() -> {
while (hasWorkToDo()) {
Object obj = getNextObject();
mark(obj);
}
}));
}
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
}
}
6. 记忆集与卡表
为了解决跨代引用的问题,JVM使用记忆集(Remembered Set)来记录从非收集区域指向收集区域的指针。卡表(Card Table)是记忆集的一种实现方式。
// 简化的卡表实现
class CardTable {
byte[] table;
int cardSize = 512; // 假设每个卡页大小为512字节
void markCard(Object address) {
int index = ((int)address) / cardSize;
table[index] = DIRTY;
}
boolean isCardDirty(Object address) {
int index = ((int)address) / cardSize;
return table[index] == DIRTY;
}
}
7. 写屏障
写屏障是一种同步屏障,用于在对象引用被更新时维护卡表或其他记忆集的状态。
// 写屏障的简化实现
class WriteBarrier {
void onObjectReferenceWrite(Object from, Object to) {
if (from.generation < to.generation) {
cardTable.markCard(from);
}
}
}
8. 垃圾回收器的优化技术
- 增量式垃圾回收:将回收过程分解成多个小步骤,减少每次GC的停顿时间。
- 并发标记:在应用程序运行的同时进行对象标记。
- 并发清理:在应用程序运行的同时清理未被标记的对象。
- 预清理:在并发标记阶段结束后,再次扫描新增的或者被修改的对象。
- 可中断的垃圾回收:允许垃圾回收过程被中断,优先处理应用程序的请求。
9. G1垃圾回收器
G1(Garbage-First)是一种面向服务端应用的垃圾收集器,它将堆内存划分为多个大小相等的独立区域(Region)。
G1的回收过程:
- 初始标记(Initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)
// G1垃圾回收器的简化流程
class G1Collector {
void collect() {
initialMark();
concurrentMark();
finalMark();
evacuate();
}
void evacuate() {
List<Region> regionsToEvacuate = selectRegions();
for (Region region : regionsToEvacuate) {
evacuateRegion(region);
}
}
}
10. ZGC(Z Garbage Collector)
ZGC是一种可扩展的低延迟垃圾收集器,它的主要特点是:
- 基于Region的内存布局
- 并发处理
- 基于颜色指针的标记
- 读屏障
// ZGC的简化实现
class ZGC {
void collect() {
pauseMarkStart();
concurrentMark();
pauseMarkEnd();
concurrentReferencesProcessing();
concurrentResetRelocationSet();
concurrentSelectForRelocation();
pauseRelocateStart();
concurrentRelocate();
}
}