类变量随类加载而生、类卸载而亡,始终存在于方法区;实例变量依附对象,对象被gc回收时才释放。二者生命周期由jvm管理,非代码行数决定。

Java 类变量(静态变量)和实例变量的生命周期,根本上不取决于你写了多少行代码,而取决于 JVM 对类和对象的管理节奏。搞清这一点,才能避免内存泄漏、空指针或“变量还在但值丢了”这类问题。
类变量:随类加载而生,随类卸载而亡
类变量用 static 修饰,属于类本身,不是某个对象。它在类第一次被主动使用(如调用静态方法、访问静态字段、new 实例等)时触发类加载,JVM 在方法区(或元空间)为其分配内存并初始化一次。
- 只要类没被卸载(绝大多数应用中,类一旦加载就不会卸载),类变量就一直存在,哪怕所有对象都销毁了,它也还在
- 多个对象共享同一个类变量,修改它会影响所有实例——这不是 bug,是设计特性
- 不能通过
obj.staticField = null或obj = null让类变量消失;设为null只是改了它的值,变量本身仍在 - 常见误用:把缓存、配置、计数器直接声明为 static,却没考虑并发安全或热部署场景下的类重载问题
实例变量:绑定对象,生死同步
实例变量定义在类里、方法外,不带 static。它不单独存在,必须依附于某个对象实例。
- 对象通过
new创建时,JVM 在堆中为整个对象分配内存,实例变量随之一起初始化(有默认值:int→0,Object→null) - 只要该对象被至少一个强引用持有(比如局部变量、集合、静态字段指向它),实例变量就有效且可读写
- 当对象变成“不可达”(例如引用被置为
null、方法结束且未返回、从 Map 中 remove),它进入 GC 待回收队列 - GC 真正执行并回收该对象时,整个对象占用的堆内存(含所有实例变量)才被释放——这个时机不确定,由 JVM 决定
销毁不是“删变量”,而是断引用 + 等回收
Java 没有 delete 或 free,所谓“销毁”其实是两个层面的事:
- 局部变量:方法结束 → 栈帧弹出 → 变量名失效 + 栈空间立刻归还(不走 GC)
- 实例变量:对象不可达 → GC 判定可回收 → 对象连同所有实例变量一并清理(可能延迟)
- 类变量:类卸载才会释放(极少见,通常只发生在 OSGi 或 Web 容器热部署时)
- 把引用设为
null不是销毁变量,只是帮 GC 更早识别“这个对象可以收了”,对短生命周期对象意义不大,但对大对象(如 byte[]、缓存 Map)能减少内存压力
实战中容易踩的坑
写代码时,真正影响系统稳定性的往往不是语法,而是对生命周期的误判:
- 静态集合(
static List<user> cache = new ArrayList()</user>)长期持有对象引用,导致本该回收的对象一直留在堆里 → 内存泄漏 - 在 Servlet 或 Spring Bean 中把 request-scoped 数据存到 static 字段 → 多线程下数据错乱或内存暴涨
- 以为
user.setName(null)就“清掉了 user”,其实只是改了字段值,user 对象本身还在堆里,引用链还在 - 在 finally 块里把流、连接等资源相关的实例变量设为
null,不如直接close()—— 关闭的是资源,不是变量
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











