对象创建和垃圾回收是同一条内存主线的两端。基础面试需要讲清对象如何创建、为何保留、何时可能回收,而不需要背对象头的每一个位。
new 大致做了什么?
可以概括为检查类是否可用、分配空间、设置零值与对象头、执行实例初始化和构造器。类初始化与对象构造不是同一步。
类加载/初始化检查 → 分配空间 → 默认零值 → 对象头 → 实例初始化与构造HotSpot 中对象常见组成包括对象头、实例数据和对齐填充。对象头包含运行时状态与类型相关信息,具体布局受 JVM、压缩指针等配置影响,不应背成永远固定的字节数。
堆分配、TLAB 与逃逸分析
堆空间可能用指针碰撞或空闲列表等方式管理。为了降低并发分配竞争,JVM 可以给线程分配 TLAB,即线程本地分配缓冲区。
TLAB 位于共享堆内,只是分配时由某个线程优先使用,不是线程栈,也不代表其中对象不能被其他线程引用。
对象语义上从堆分配理解即可。JIT 通过逃逸分析可能做标量替换、消除部分分配或锁,所以也不要说“每个 new 一定在堆上产生一个独立可见对象”。
对象什么时候晋升老年代?
在传统分代收集器中,新对象通常先进入新生代,经过回收仍存活的对象可能留在 Survivor 区,并在达到条件时晋升。空间不足、对象大小等也会影响分配与晋升。
对象晋升年龄不是所有收集器统一固定的 15;是否分代、阈值和大对象路径要看收集器与配置。G1 的 Region 组织也不能简单当成固定连续的 Eden 和 Old 大块区域。
四种引用
| 类型 | 保留与回收特点 | 典型理解 |
|---|---|---|
| 强引用 | 存在 GC Roots 可达的强引用路径时,不因内存紧张而直接回收对象 | 普通对象引用 |
| 软引用 | 对象只软可达时,可在内存压力下清理 | 不宜当作可控容量的缓存替代品 |
| 弱引用 | 对象只弱可达时,GC 处理时可以清理 | 不阻止对象回收的关联关系 |
| 虚引用 | get 不能获取目标对象,配合引用队列观察回收相关通知 | 资源清理机制相关用途 |
弱引用不是“每次 GC 都清理所有弱引用对象”,如果对象还有强引用路径就不能按只弱可达处理。引用队列的通知也不意味着立即释放任意外部资源,业务仍需正确的清理流程。
面试回答模板
new 对象大致经过类检查、内存分配、零值设置、对象头设置和构造初始化。TLAB 是堆中的线程本地分配缓冲区,用于降低分配竞争。分代收集器中对象可以从新生代晋升到老年代,但阈值依赖配置。引用强度决定可达性处理:强引用维持存活,软引用可在压力下清理,弱引用不保活,虚引用主要配合队列用于回收通知。
自测
- TLAB 在栈上吗?不在,属于堆。
- 只有达到 15 岁才会晋升吗?不是。
- WeakReference 指向的对象仍被其他强引用持有,能因此被回收吗?不能。