)
垃圾回收算法是JVM管理堆内存的核心策略。它的目标是自动识别并回收不再使用的对象让开发者无需手动管理内存。我们先理解一个前提堆内存中存放的是所有new出来的对象实例。垃圾回收GC主要关注这块区域。一、垃圾判断算法哪些对象需要被回收在执行回收前JVM需要先判断哪些对象是垃圾。1. 引用计数法Reference Counting原理每个对象维护一个计数器当有引用指向它时1引用失效时-1。计数器为0时表示可回收。优点实现简单判断高效。致命缺陷无法解决循环引用问题。例如A引用BB引用A但外部没有其他引用指向它们理论上应该被回收但引用计数都不为0。结果JVM没有采用此算法。2. 可达性分析算法Reachability Analysis这是主流JVM采用的算法。原理从一组称为GC Roots的根对象出发通过引用链向下搜索能到达的对象标记为存活未到达的对象就是可回收的垃圾。GC Roots包括虚拟机栈栈帧中的局部变量表中引用的对象方法区中静态属性引用的对象方法区中常量引用的对象如字符串常量池中的引用本地方法栈中JNI引用的对象Java虚拟机内部的引用基本数据类型对应的Class对象、常驻异常对象等所有被同步锁synchronized持有的对象图示理解对象A ----- GC Root (main方法局部变量) ↓ 对象B ----- 对象C (循环引用但无外部引用) ↓ ↓ 对象D 对象E 可达A、B、D → 存活 不可达C、E → 垃圾二、垃圾回收算法核心四种确定哪些是垃圾后JVM使用以下算法来回收内存。1. 标记-清除Mark-Sweep这是最基础的算法。步骤标记遍历所有GC Roots标记所有可达对象存活对象。清除线性遍历堆内存回收所有未标记的对象。优点实现简单不需要移动对象。缺点效率问题标记和清除两个过程都需要遍历对象越多效率越低。内存碎片清除后产生大量不连续的内存碎片导致后续分配大对象时无法找到连续空间触发另一次GC。[存活][垃圾][存活][垃圾][垃圾][存活] 标记后[存活][标记][存活][标记][标记][存活] 清除后[存活][ 空 ][存活][ 空 ][ 空 ][存活] → 碎片化严重2. 复制Copying为了解决碎片问题而设计现代JVM的新生代主要使用此算法。原理将内存划分为大小相等的两块From区和To区每次只使用其中一块。当一块用满时将存活对象复制到另一块然后一次性清空原整块内存。过程所有对象分配在From区。From区满时标记存活对象。将存活对象复制到To区并保持连续排列。清空From区整块变为垃圾。交换角色From区和To区互换。优点无碎片存活对象紧凑排列。高效只需处理存活对象复制成本低新生代对象存活率低。缺点内存利用率低始终有一半空间空闲。不适用老年代老年代对象存活率高复制成本大。From区(使用中) To区(空闲) [存活1][存活2][垃圾] [空][空][空][空][空] 复制存活对象后 From区(待清空) To区(使用中) [垃圾][垃圾][垃圾] [存活1][存活2][空][空][空] 清空并交换后 To区变为下一轮的From区3. 标记-整理Mark-Compact解决标记-清除的碎片问题同时避免复制的空间浪费老年代主要使用此算法。步骤标记同标记-清除标记存活对象。整理将所有存活对象向一端移动使它们紧凑排列。清理边界清除边界以外的内存。优点无碎片内存利用率高不需要额外空间。缺点移动对象需要暂停所有用户线程Stop The World整理成本高于标记-清除。[存活][垃圾][存活][垃圾][存活][垃圾] 标记[存活][标记][存活][标记][存活][标记] 整理将所有存活对象向左移动 [存活][存活][存活][空][空][空] → 连续且紧凑4. 分代收集Generational Collection这是商业JVM实际采用的策略不是独立算法而是对以上算法的组合应用。核心假设弱分代假说大部分对象朝生夕灭创建后很快变为垃圾。少数对象长期存活熬过多次GC的对象会继续存活很久。堆内存分代结构-------------------------------------------- | 新生代 | 老年代 | | Eden | S0 | S1 | | -------------------------------------------- 年轻代占堆1/3又分为Eden(8)、From(1)、To(1) 老年代占堆2/3回收策略新生代对象存活率低使用复制算法。Eden区满时触发Minor GC存活对象复制到Survivor区。熬过多次默认15次GC后晋升到老年代。老年代对象存活率高无额外空间分配担保使用标记-清除或标记-整理。老年代满时触发Major GC / Full GC通常比Minor GC慢10倍以上。三、JVM中的常见垃圾回收器实现不同的GC算法由具体的垃圾回收器实现常见的包括回收器适用区域算法特点Serial新生代复制单线程GC时Stop The World适用于客户端模式ParNew新生代复制Serial的多线程版本服务端常用Parallel Scavenge新生代复制关注吞吐量可控制GC时间Serial Old老年代标记-整理Serial的老年代版本Parallel Old老年代标记-整理Parallel Scavenge的老年代版本CMS老年代标记-清除并发回收追求低停顿但有碎片问题G1全堆复制标记-整理将堆分为Region可预测停顿时间ZGC全堆染色指针读屏障停顿时间10ms支持TB级堆四、一个完整对象的生命周期追踪Object obj new Object(); // 1. 对象分配在Eden区 obj null; // 2. 对象失去引用成为垃圾 System.gc(); // 3. 触发Minor GC对象被回收实际流程分配new Object()在Eden区分配内存。晋升如果对象熬过多次Minor GC会从Eden → Survivor0 → Survivor1最终晋升到老年代。回收对象在新生代Minor GC时通过复制算法回收。对象在老年代Full GC时通过标记-清除或标记-整理回收。五、关键概念补充Stop The WorldSTWGC发生时所有用户线程暂停只有GC线程工作。所有垃圾回收器都会发生STW只是暂停时间长短不同。直接内存Direct MemoryNIO的DirectByteBuffer使用的堆外内存不受GC管理需要手动释放。内存泄漏与内存溢出内存泄漏对象不再使用但仍被GC Roots引用无法被回收例如静态集合持有无用对象。内存溢出堆内存耗尽无法分配新对象抛出OutOfMemoryError。