Java对象在new、反射、反序列化等操作时创建,最常见是new;对象可被回收当且仅当无GC Roots可达的强引用链;finalize已弃用,应使用AutoCloseable或Cleaner;回收时机由GC器决定,不可控。

对象什么时候被创建
Java对象在执行 new 操作符、反射调用 Class.newInstance()(已弃用)、Constructor.newInstance()、反序列化或使用某些字节码操作库(如 ASM)时被创建。最常见的是 new —— 它会触发类加载(若未加载)、分配堆内存、调用构造方法初始化字段。
注意:对象创建不等于立即可用。如果构造方法抛出异常,对象虽已分配内存但不会完成初始化,JVM 会丢弃该实例并触发垃圾回收准备(实际是否回收取决于引用状态)。
-
new是唯一由开发者直接控制的创建入口;其他方式本质仍是 JVM 内部调用构造器 - 数组也是对象,
new int[10]或new String[5]同样走对象创建流程 - 局部变量声明
MyObj obj;不创建对象,只是声明一个引用变量
对象何时进入“可被回收”状态
对象变成“垃圾”的判定标准不是它是否“用完了”,而是是否还存在从 GC Roots 可达的强引用链。GC Roots 包括:虚拟机栈中引用的对象、本地方法栈中 JNI 引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、以及 synchronized 锁持有的对象。
常见误判场景:
立即学习“Java免费学习笔记(深入)”;
- 集合里存了对象但忘了清理,导致长期持有强引用
- 内部类隐式持有了外部类实例(非静态内部类),造成外部对象无法释放
- ThreadLocal 变量未调用
remove(),在线程池复用场景下引发内存泄漏 - 监听器/回调注册后未注销,形成“悬挂引用”
finalize() 方法真的会被调用吗
不会,或者说几乎不会。自 Java 9 起,finalize() 已被标记为 @Deprecated,Java 18 开始默认禁用。即使启用(通过 -XX:+EnableFinalization),它的执行也不保证及时性、顺序性或必然性——JVM 可能在退出前都不调用它。
替代方案更可靠:
- 实现
AutoCloseable,配合 try-with-resources 管理资源 - 使用
Cleaner(Java 9+)注册清理逻辑,它基于虚引用(PhantomReference),不阻塞 GC - 避免依赖任何“最后机会”回调做关键释放(如关闭文件、释放锁)
private static final Cleaner cleaner = Cleaner.create();
private final Cleaner.Cleanable cleanable;
public MyResource() {
this.cleanable = cleaner.register(this, new ResourceCleanup(this));
}
private static class ResourceCleanup implements Runnable {
private final MyResource resource;
ResourceCleanup(MyResource resource) { this.resource = resource; }
public void run() { resource.doCleanup(); }
}
对象真正被回收的时机不可控
即使对象已不可达,JVM 也不会立刻回收。是否回收、何时回收、是否压缩内存,完全由所选垃圾收集器(如 G1、ZGC、Shenandoah)和运行时参数决定。你只能影响概率(比如调用 System.gc() 仅是建议,JVM 可忽略)。
这意味着:
- 不能靠“对象被回收”来推断业务逻辑状态(例如认为某缓存失效了)
- 频繁短生命周期对象会加剧 Minor GC 频率,但大对象可能直接进入老年代,跳过年轻代收集
- 对象大小影响分配策略:超过
-XX:PretenureSizeThreshold的对象会直接分配到老年代(G1 中对应-XX:G1HeapRegionSize相关逻辑)
真正需要关注的,是引用关系的设计与显式资源管理,而不是等待某个“生命周期终点”。JVM 只负责内存空间的回收,不负责业务语义的终结。










