类变量生命周期始于类加载完成、终于类卸载,仅在类首次主动使用时初始化一次,jdk 8+ 存于元空间,卸载需满足实例全回收、classloader被回收、class对象无强引用三条件。

类变量(即静态变量)的生命周期完全依附于其所属类的生命周期。它不是在对象创建时诞生,也不是随方法调用而产生,而是从类被 JVM 加载进内存那一刻起就存在,直到该类被彻底卸载才真正消失。
类变量何时被分配和初始化
类变量在类加载的“准备阶段”分配内存并设默认值(如 0、null、false),在“初始化阶段”执行显式赋值和静态代码块——这个过程只发生一次,且与任何对象实例无关。
- 分配位置:JDK 8+ 在元空间(Metaspace),之前在方法区(PermGen)
- 初始化时机:仅当类首次主动使用(如首次访问静态字段、调用静态方法、new 实例等)触发类初始化时才执行
- 不随 new 出的对象数量变化,也不因方法调用栈进出而增减
类卸载是类变量清理的唯一前提
类变量不会在程序运行中“自动销毁”,只有当类本身被 JVM 卸载,它占用的内存才会释放。而普通应用中,系统类或由 AppClassLoader 加载的类几乎不会卸载——它们会一直存活到 JVM 退出。
- 卸载条件严格:必须同时满足三项——该类所有实例已被回收、加载它的 ClassLoader 已被回收、该类的 Class 对象无任何强引用(包括反射、static 字段持有、JNI 引用等)
- 常见阻断点:静态变量持有了某个对象的引用,而该对象又反向引用了该类的 Class 对象(例如通过 getClass() 或 XXX.class),形成循环引用链,导致 Class 对象不可达判定失败
- 插件/热更新场景需特别关注:自定义 ClassLoader 加载的类若未正确清理引用,极易造成元空间内存持续增长
如何验证类变量是否已清理
类卸载和静态变量清理无法直接观测,但可通过工具链辅助判断:
-
jcmd:查看类加载器统计(
jcmd <pid> VM.native_memory summary</pid>或VM.class_hierarchy) - JFR(Java Flight Recorder):开启 class-loading 事件,追踪类加载/卸载时间点
- VisualVM / MAT:分析堆转储(heap dump),检查 ClassLoader 实例是否仍存活、Class 对象是否被引用
- 日志配合:在类的
finalize()(不推荐)或 Cleaner 注册回调中埋点——注意:Class 对象本身不走常规 GC finalize 流程,需依赖类卸载钩子(如 Instrumentation.addTransformer)
实际开发中的关键提醒
别指望 static 变量跨 JVM 进程保留数据。每次启动都是全新内存空间,上次运行的静态值全部丢失。需要持久化,必须落地到文件、数据库或外部存储。
- 避免在 static 字段中缓存大对象(如 Map
),尤其当 key 来自用户输入或动态生成时 - 使用 WeakReference 或 SoftReference 包裹静态缓存,降低内存滞留风险
- 在模块卸载前,主动置空静态引用(
MyClass.cache = null;),协助 ClassLoader 和 Class 对象达成可回收状态 - 测试类卸载行为时,务必使用自定义 ClassLoader,并确保其自身不被上层 ClassLoader 持有











