在Java的世界里,程序运行时的内存就像一间不断产生垃圾的房间。如果没有“清洁工”及时清理,垃圾堆积会导致房间无法容纳新物品,最终程序崩溃。而JVM的垃圾回收(GC)机制,正是这位隐形的“扫地僧”,默默守护着内存空间的整洁。今天,我们就来揭秘这位“清洁工”的工作手册——垃圾回收算法的实现原理。

垃圾回收的第一步是判断哪些对象是“垃圾”。JVM用两种经典方法解决这个问题:
引用计数法 每个对象自带计数器,记录被引用的次数。当计数器归零时,对象被标记为垃圾。但这种方法存在致命缺陷:若对象A引用B,B又引用A(循环引用),即使它们已不被外界使用,计数器仍不为零,导致内存泄漏。就像两个互相抓着衣角的人,谁都不肯松手,结果双双困在内存中。可达性分析法(根搜索算法) JVM通过一系列“根对象”(如虚拟机栈中的局部变量、静态属性等)作为起点,逐层扫描引用链。无法被根对象触及的对象即为垃圾。这种方法彻底解决了循环引用问题,但需要暂停用户线程(Stop-The-World,STW),就像清洁工工作时让所有人暂时离开房间。小知识:即使对象被判定为垃圾,JVM还会给它一次“临终关怀”——调用finalize()方法,但该方法最多执行一次,且不保证及时性。
四大核心算法:清洁工的“独门秘籍”1. 标记-清除算法:简单粗暴的“大扫除”原理:先标记存活对象,再清除未标记的垃圾。缺点:产生内存碎片,就像房间角落堆满碎纸片,即使总空间足够,也可能无法放下大件家具。2. 复制算法:空间换时间的“乾坤大挪移”原理:将内存分为两块,每次只用其中一块。垃圾回收时,将存活对象复制到另一块,原空间直接清空。优势:无碎片问题,适合新生代(98%对象“朝生夕死”)。但浪费50%内存,就像为了打扫一间房,必须再准备一间空房。优化:Appel式回收将新生代划分为Eden区(80%)和两个Survivor区(各10%),空间利用率提升至90%。3. 标记-整理算法:老年代的“整理术”原理:标记存活对象后,将其向内存一端移动,清理边界外的空间。适用场景:老年代对象存活率高,整理后内存连续,但移动对象导致额外开销。4. 分代收集算法:因材施教的“智慧管家”设计理念:根据对象生命周期划分内存区域:新生代:采用复制算法,快速清理短命对象。老年代:采用标记-清除或标记-整理算法,低频处理长寿对象。晋升机制:对象在Survivor区每熬过一次GC,年龄+1,默认15岁后晋升老年代。 比喻:就像学校分年级,新生班(新生代)频繁考试淘汰,毕业班(老年代)只偶尔大考。主流垃圾收集器:清洁工具的“进化史”1. Serial收集器:单线程的“老扫帚”特点:单线程串行回收,全程STW。适合客户端程序或小内存场景,如同用一把扫帚慢慢清扫。参数:-XX:+UseSerialGC。2. ParNew/CMS组合:高并发的“双人组”ParNew:多线程版Serial,专攻新生代,与CMS搭档。CMS(Concurrent Mark Sweep):以低延迟为目标,分四阶段:初始标记(STW)→并发标记→重新标记(STW)→并发清除。但存在内存碎片和浮动垃圾问题,如同边打扫边有人扔新垃圾。3. G1收集器:分区管理的“智能机器人”创新点:将堆划分为2048个Region(默认2MB),优先回收垃圾最多的区域(Garbage-First)。混合回收:Young GC处理新生代,Mixed GC兼顾部分老年代,避免Full GC。优势:支持最大暂停时间预测(-XX:MaxGCPauseMillis),适合大内存服务器。4. ZGC/Shenandoah:低延迟的“光速战士”ZGC:在JDK15中正式发布,通过染色指针和读屏障技术,实现亚毫秒级停顿,即使堆内存达TB级。Shenandoah:与ZGC类似,但采用Brooks指针,RedHat贡献给OpenJDK,适合响应敏感场景。对比:


从Serial到ZGC,垃圾回收算法的演进不仅是技术的升级,更是对“效率与公平”的永恒追求——既要快速清理垃圾,又不能让程序“卡顿”。正如生活,我们也在不断平衡“断舍离”与“珍惜当下”。下一次你的Java程序流畅运行时,别忘了向这位幕后英雄致敬!