Loading... # JVM垃圾回收标记算法详解 ## 一、标记算法核心原理 ### 1.1 可达性分析模型 ```mermaid graph TD A[GC Roots] --> B[活动线程栈帧] A --> C[系统类加载器] A --> D[静态变量引用] A --> E[JNI全局引用] B --> F[对象实例] C --> G[Class对象] D --> H[常量池引用] E --> I[本地方法对象] ``` 📌 **判定依据**:从<span style="color:red">GC Roots</span>出发,遍历引用链不可达的对象判定为可回收 ### 1.2 三色标记法(Tri-color Marking) | 颜色状态 | 含义 | 内存状态 | | -------- | -------------------- | ---------- | | 白色 | 未访问对象 | 待回收候选 | | 灰色 | 已扫描但子引用未处理 | 中间状态 | | 黑色 | 完全处理对象 | 存活对象 |  ## 二、主流标记算法实现 ### 2.1 引用计数法(传统方案) ```java class ReferenceCounter { int count = 0; void addReference() { count++; // 引用增加 } void release() { if(--count == 0) { System.gc(); // 触发回收 } } } ``` 🚫 **缺陷**: - 循环引用无法回收(A→B→A) - 计数器维护开销大(每次引用变更需更新) ### 2.2 追踪式算法(现代JVM方案) #### 2.2.1 标记-清除(Mark-Sweep) ```java // 伪代码实现 void markSweep() { markPhase(); // 标记存活对象 sweepPhase(); // 清理未标记区域 } void markPhase() { for(Object obj : reachableObjects) { obj.marked = true; // 标记可达对象 } } void sweepPhase() { for(Object obj : heap) { if(!obj.marked) { free(obj); // 释放内存 } } } ``` ⚠️ **问题**:内存碎片化(需配合压缩算法) #### 2.2.2 标记-复制(Mark-Copy) ```java // 新生代回收示例 void youngGC() { Object[] fromSpace = ...; // Eden+Survivor Object[] toSpace = new Object[fromSpace.length]; int ptr = 0; for(Object obj : fromSpace) { if(isMarked(obj)) { // 存活对象复制 toSpace[ptr++] = obj; } } swapSpaces(); // 交换空间指针 } ``` 🔁 **特点**:空间换时间(默认存活率<10%) ## 三、现代GC算法优化 ### 3.1 卡表技术(Card Table) ```java // 跨代引用处理 byte[] cardTable = new byte[HEAP_SIZE / 512]; // 每512字节对应1字节卡表 void writeBarrier(Object oldRef, Object newRef) { if(oldRef.inOldGen() && newRef.inYoungGen()) { cardTable[oldRef.address >> 9] = 1; // 标记脏卡 } } ``` 💡 **作用**:减少全堆扫描,加速<span style="color:red">跨代引用</span>定位 ### 3.2 增量更新 vs 原始快照 | 方案 | 实现原理 | 适用场景 | | -------- | ---------------- | ------------- | | 增量更新 | 记录新增引用关系 | CMS回收器 | | 原始快照 | 保留删除前引用链 | G1/Shenandoah | ## 四、并行标记优化策略 ### 4.1 位图标记(BitMap Marking) ```c // HotSpot实现片段 typedef struct { uintptr_t* bitmap; // 位图数组 size_t size; // 堆内存大小 } BitMap; void set_bit(BitMap* map, uintptr_t addr) { size_t index = (addr - heap_base) >> 12; // 按4KB分块 map->bitmap[index/64] |= 1ULL << (index%64); } ``` 🚀 **优势**:内存占用减少80%(对比对象头标记) ### 4.2 并行标记工作流 ```mermaid graph LR A[初始标记] --> B[根节点枚举] B --> C[并行标记] C --> D[最终标记] D --> E[清理阶段] ``` - **初始标记**:STW暂停(约10ms) - **并行标记**:并发执行(主要耗时阶段) - **最终标记**:处理剩余引用 ## 五、各版本GC算法演进 | JDK版本 | 主要改进 | 标记算法优化 | | ------- | -------------------- | ----------------- | | 7 | G1实验性引入 | 增量并行标记 | | 8 | 移除PermGen | 卡表分区优化 | | 11 | ZGC生产可用 | 颜色指针+负载屏障 | | 17 | Shenandoah成标准特性 | 并发引用处理 | | 21 | Generational ZGC | 分代式颜色指针 | ## 六、性能调优关键参数 ```properties # 常用JVM参数 -XX:+UseParallelGC # 并行标记清除 -XX:ParallelGCThreads=4 # 并行线程数 -XX:+UseG1GC # G1分代收集 -XX:G1ConcRefinementThreads=2 # 并发标记线程 -XX:InitiatingHeapOccupancyPercent=45 # 触发阈值 ``` ## 七、内存泄漏检测示例 ```java // 典型内存泄漏场景 public class LeakDemo { static List<byte[]> cache = new ArrayList<>(); public static void main(String[] args) { while(true) { cache.add(new byte[1024*1024]); // 持续分配内存 System.gc(); // 无法回收被cache引用的对象 } } } ``` 🔍 **分析方法**: 1. 使用 `jmap -histo:live <pid>`查看堆内存 2. 分析MAT(Memory Analyzer Tool)支配树 ```mermaid graph TD A[标记算法] --> B[引用追踪] A --> C[引用计数] B --> D[追踪式GC] D --> E[标记-清除] D --> F[标记-复制] D --> G[标记-整理] B --> H[分代收集] H --> I[年轻代] H --> J[老年代] ``` 最后修改:2025 年 04 月 03 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏