排查static块类初始化死锁需识别互相触发初始化的依赖链,通过jstack定位WAITING on java.lang.Class的线程栈,梳理静态依赖图,移出static块中双向调用逻辑,改用Holder模式或@PostConstruct延迟初始化,并通过并发测试和JFR监控提前发现隐患。
排查 static 块引发的类初始化死锁,关键在于识别“互相触发初始化”的依赖链。这类死锁不抛异常、线程卡在 WAITING 状态,但 JVM 已内置同步机制,问题根源不在锁本身,而在 `
` 方法内部的跨类调用逻辑。
看线程栈,定位阻塞点
用
抓取线程快照,重点搜索以下特征:
线程状态为
WAITING on java.lang.Class
(不是 Object 或自定义锁)
栈顶出现
或
其上层调用链中嵌套着两个或多个类的
,例如:
at com.example.A.(A.java:5)at com.example.B.(B.java:8)at java.lang.Class.forName0(Native Method)
查静态依赖,画初始化图
静态块中若调用其他类的非编译期常量或静态方法,就会触发对方初始化。需人工梳理:
找出所有含
的类,检查其中是否直接访问了其他类的静态字段/方法
确认这些被访问的字段是否为
编译期常量
(
✅ 不触发;
❌ 触发)
对存在双向调用的类(如 A.static{} 调 B.xxx,B.static{} 又调 A.xxx),标记为高风险对
改代码,切断循环依赖
修复原则是让初始化过程单向、延迟或惰性:
把互相调用的逻辑从
中移出,改为首次使用时再初始化(例如用静态内部类 Holder 模式)
若必须在类加载时准备数据,改用
或 Spring 的 @PostConstruct 替代硬编码调用
避免在 static 块中执行耗时操作(如网络请求、文件读取、sleep),防止延长锁持有时间,放大竞争窗口
对工具类,优先用枚举或 final 类 + 私有构造,配合静态字段直接赋值(无逻辑),而非 static 块
加监控,提前发现隐患
上线前可做轻量级验证:
写一个测试用例,用两个线程分别并发调用疑似互相依赖的类的静态方法,观察是否 hang 住
在 CI 流程中集成
和 jstack 自动分析脚本,检测 WAITING 线程是否集中于 Class 初始化路径
使用 JFR(Java Flight Recorder)开启
事件,观察类初始化耗时和阻塞关系
jstack at java.lang.Class.forName0(Native Method)at java.lang.ClassLoader.loadClassstatic{...}public static final String X = "abc";public static final String Y = UUID.randomUUID().toString();static{}ServiceLoaderjcmd VM.native_memory summary jdk.ClassLoading