Java GC是分代、可调优的运行时机制,通过可达性分析判定垃圾,新生代采用Eden/S0/S1复制回收,G1与ZGC设计目标不同:G1基于Region预测回收,ZGC依赖着色指针实现超低延迟。

Java 垃圾回收(GC)不是“自动清内存”这么简单——它是一套有策略、分代、可调优的运行时机制,不理解触发条件和回收范围,很容易在 OutOfMemoryError 或 Stop-The-World 暂停时措手不及。
哪些对象会被判定为“垃圾”?
Java 不用引用计数,而是用**可达性分析**:从 GC Roots 出发,不可达的对象才可能被回收。GC Roots 包括:
-
栈帧中的局部变量表引用的对象 -
方法区中静态属性引用的对象 -
方法区中常量引用的对象(如字符串常量池里的"abc") -
本地方法栈中 JNI引用的对象
注意:finalize() 方法已被标记为 @Deprecated,且仅最多执行一次,不能当作资源清理主逻辑;依赖它延迟回收,大概率导致内存堆积。
为什么新生代要分 Eden、S0、S1?
绝大多数对象朝生暮死,分代 + 空间划分是为了让 Minor GC 更快——复制算法比标记清除/整理更适合小空间高频回收。
立即学习“Java免费学习笔记(深入)”;
典型分配与回收流程:
- 新对象优先分配到
Eden区 - Minor GC 触发时,存活对象被复制到
Survivor(S0 或 S1),年龄 +1 - 对象年龄达到
MaxTenuringThreshold(默认 15)或 Survivor 空间不足,直接晋升到Old区 -
S0和S1总是保持一个为空,用于复制交换
如果 Eden 区填满但 Minor GC 后仍无法腾出足够空间,会触发 Allocation Failure 并尝试分配担保(如直接进老年代),失败则抛 OutOfMemoryError: Java heap space。
篇文章是针对git版本控制和工作流的总结,如果有些朋友之前还没使用过git,对git的基本概念和命令不是很熟悉,可以从以下基本教程入手: Git是分布式版本控制系统,与SVN类似的集中化版本控制系统相比,集中化版本控制系统虽然能够令多个团队成员一起协作开发,但有时如果中央服务器宕机的话,谁也无法在宕机期间提交更新和协同开发。甚至有时,中央服务器磁盘故障,恰巧又没有做备份或备份没及时,那就可能有丢失数据的风险。感兴趣的朋友可以过来看看
常见的 GC 日志怎么看?
加 -XX:+PrintGCDetails -Xloggc:gc.log 后,一行典型日志:
[GC (Allocation Failure) [PSYoungGen: 123456K->12345K(131072K)] 123456K->123456K(262144K), 0.0123456 secs]
关键字段含义:
-
PSYoungGen:使用 Parallel Scavenge 收集器的新生代 -
123456K->12345K:GC 前后新生代占用,差值≈回收量 -
(131072K):新生代总容量 -
123456K->123456K:整个堆 GC 前后占用(说明老年代没动) -
0.0123456 secs:Stop-The-World 时间
若发现 Full GC 频繁或老年代占用持续上涨,大概率存在内存泄漏,不是调大堆就能解决的。
G1 和 ZGC 的核心差异在哪?
它们都面向低延迟,但设计哲学不同:
-
G1把堆划成固定大小 Region,通过预测模型选择收益最高的 Region 回收(Garbage-First),仍需 STW,但可控;适合 4–64GB 堆,要求暂停时间 -
ZGC使用着色指针 + 读屏障,大部分阶段并发执行,STW 控制在,支持 TB 级堆;但要求 JDK 11+,且对 Linux/proc/sys/vm/max_map_count有最低要求(通常需 ≥ 1048576)
别只看“ZGC 更先进”,实际压测中若应用大量使用反射或 JNI,ZGC 可能因读屏障开销反而不如 G1 稳定。
GC 行为高度依赖具体收集器、JDK 版本、堆结构和业务对象生命周期模式。线上调优前,先用 jstat 或 JFR 看真实行为,而不是照搬“-XX:+UseG1GC”就以为万事大吉。









