Java GC通过可达性分析(从GC Roots出发)判断对象是否为垃圾,而非引用计数;新生代用复制算法因存活率低,老年代用标记-整理避免碎片;软/弱/虚引用仅影响回收时机,不改变可达性。

Java里的垃圾回收(GC)机制,本质就是JVM自动识别并释放堆中“没人再用”的对象内存,不用你写 free 或 delete ——但它的自动性不等于“无感”,一旦理解偏差或配置不当,很容易触发频繁停顿、OOM甚至服务抖动。
怎么判断一个对象是不是“垃圾”?靠可达性分析,不是引用计数
很多人误以为“引用没了就立刻可回收”,其实JVM根本不看引用个数。它从一组固定起点(GC Roots)出发,比如:栈帧里的局部变量、静态字段、JNI引用等,顺着所有引用链往下搜。搜不到的对象,才被标记为垃圾。
- 引用计数法早被HotSpot弃用——它解决不了循环引用问题,比如
A.obj = B且B.obj = A,两个对象引用数都不为0,但实际已脱离业务逻辑 -
System.gc()只是建议,JVM可以忽略;真正触发GC的往往是Eden区填满、老年代空间不足等内部阈值 - 哪怕写了
obj = null,如果该对象还在其他地方被强引用(比如缓存Map里存着),它依然不是垃圾
为什么新生代用复制算法,老年代却用标记-整理?
这是由对象存活率差异决定的——不是设计者偏好,而是数据规律倒逼出来的选择。
- 新生代中约95%的对象朝生暮死,复制算法只需搬运少量存活对象(比如从Eden+Survivor0拷到Survivor1),效率高且无碎片
- 老年代对象平均年龄大、存活率高,复制成本太高;标记-整理虽要移动对象,但能避免碎片导致的大对象分配失败(
Full GC前常因“空间碎片”而非“总空间不足”触发) - 注意:CMS收集器在老年代用的是标记-清除,结果就是容易产生碎片,最终被迫退化成Serial Old做标记-整理——这就是为什么JDK9后CMS被移除
常见误操作:把软/弱/虚引用当“自动清理开关”用
这三类引用不是GC策略开关,而是不同强度的“保留许可”。它们只影响GC时是否回收,不改变对象是否可达。
立即学习“Java免费学习笔记(深入)”;
-
SoftReference:内存快不够时才回收,适合缓存,但别指望它精准控制内存峰值 -
WeakReference:只要发生GC(哪怕是Young GC),就可能被清掉,适合临时映射(如WeakHashMap),但不能用于兜底逻辑 -
PhantomReference:无法通过它拿到对象实例,只能配合ReferenceQueue监听回收事件——想靠它“及时释放资源”?得自己确保资源关闭逻辑不依赖对象状态
GC不是黑盒魔法,它是以堆结构为前提、受对象生命周期分布约束、对应用线程有真实侵入的系统级过程。调优时盯着GC log比背算法更重要,而最容易被忽略的,其实是那些没被正确移除的监听器、静态集合和ThreadLocal——它们让本该死亡的对象,悄悄挂在了GC Roots上。








