Skip to content

Java 对象通常怎样分配,垃圾收集器又如何判断对象可以回收?

题型:追问 · 区分语言语义、JIT 优化和 HotSpot 的具体实现。

建议回答

对象在语义上创建于 Java 堆,但具体实现会先看对象能否被标量替换或消除;真正需要分配时,线程通常优先在自己的 TLAB 中通过指针碰撞快速分配,TLAB 不够再走共享分配路径。大对象、分配失败后的处理以及是否直接进入老年代取决于收集器、堆布局和 JVM 参数,不能概括成固定规则。

GC 不依赖引用计数,而是从 GC Roots 做可达性分析。常见根包括线程栈中的活动引用、静态字段、JNI 引用和 JVM 内部引用。一个对象从根不可达后才具备被回收的条件;软、弱、虚引用还会按各自语义在引用处理阶段参与判断。

设计边界

  • 栈上分配通常是逃逸分析和标量替换带来的优化结果,不是 Java 规范承诺。
  • finalize 已被弃用,资源释放应使用 try-with-resources、显式关闭或 Cleaner 等受控机制。
  • “不可达”不等于业务上立刻释放,实际回收时间由收集器周期决定。

进一步追问

  • 分代假设为什么有效,跨代引用如何避免扫描整个老年代?
  • 并发标记期间引用关系变化,收集器怎样避免漏标?

易错点

  • 不要把所有新对象都说成一定先进入 Eden,也不要把大对象去向说成所有收集器都一致。
  • 不要认为循环引用会导致 Java GC 无法回收;只要整组对象从 GC Roots 不可达,就可以被回收。

知识关系

从当前问题继续深入,或者回到提出这个问题的知识入口。