Java对象生命周期取决于GC Roots可达性而非创建或显式释放;对象真正创建需内存分配且构造方法执行完毕;未断开引用链(如静态集合、内部类、ThreadLocal)会导致延迟回收;finalize已废弃,应使用try-with-resources、Cleaner或PhantomReference。

Java对象的生命周期不是“创建→使用→销毁”这么线性,关键在于引用关系是否还被GC Roots可达,而不是你有没有调用 new 或显式“释放”。对象真正进入可回收状态,往往比你想象中晚得多。
对象何时算“已创建”?不只是 new 完就完事
对象在堆上分配内存并完成初始化(即构造方法执行完毕)才算真正创建完成。但要注意:
-
new表达式执行时,若堆空间不足且 GC 后仍无法满足,会直接抛出OutOfMemoryError,此时对象根本没创建成功 - 构造方法中若抛出异常(如
NullPointerException或自定义异常),JVM 会回滚部分初始化:对象内存可能已分配,但引用不会赋值给变量,该对象立即成为垃圾 - 使用
Unsafe.allocateInstance()可绕过构造方法分配对象,此时对象处于“已分配但未初始化”状态——它 technically 存在,但字段全为默认值,且不推荐日常使用
为什么对象没被回收?先查引用链是否断开
GC 不看“你是不是不用了”,只看从 GC Roots(如栈帧中的局部变量、静态字段、JNI 引用等)出发能否找到该对象的引用路径。常见陷阱:
- 静态集合类(如
static Map)长期持有对象引用,即使业务逻辑早已弃用,对象也无法回收cache - 内部类默认持外部类强引用,若将非静态内部类实例注册为监听器或提交到线程池,容易导致外部对象内存泄漏
- ThreadLocal 变量在线程结束前未调用
remove(),其Entry中的 value 会随线程存活而无法回收(尤其在线程池场景下)
finalize() 已废弃,替代方案是 Cleaner 或 PhantomReference
Object.finalize() 自 Java 9 起标记为 @Deprecated,Java 18 开始默认禁用,且行为不可靠(不保证执行、不保证及时、可能不执行)。正确做法:
立即学习“Java免费学习笔记(深入)”;
- 资源清理应通过
try-with-resources(实现AutoCloseable)或显式close()完成 - 若需在对象被回收时触发轻量级回调(如日志、统计),用
Cleaner:Cleaner cleaner = Cleaner.create(); cleaner.register(obj, new CleaningTask());
- 需要更精细控制回收时机(比如等待对象不可达后做清理),可用
PhantomReference配合ReferenceQueue,但注意它无法获取原始对象,仅作通知用途
对象生命周期的复杂性,主要藏在引用关系的隐式维持里——一个没清空的 WeakHashMap 的 key、一个忘了 unregister 的观察者、甚至一个未关闭的 Stream,都可能让本该回收的对象多活几轮 GC。别依赖 finalize,也别假设“没用了就自动没了”。










