问题
今天在排查一个线上的问题,线上的一个应用在初始化一个类的静态字段的时候出现了 NoClassDefFoundError
,并且在导致 NoClassDefFoundError
出现的根本原因消失后,后续再次尝试初始化这个类的时候,持续出现了 NoClassDefFoundError
。
于是怀疑 JVM 是不是对一个类的 NoClassDefFoundError
做了缓存,在第一次加载这个类出现 NoClassDefFoundError
以后,后续再尝试加载就直接抛出 NoClassDefFoundError
。
实验
为了证实自己的猜想,尝试设计了一个简单的实验,一个涉及三个类
1 2 3 |
|
1 2 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
上述类的的作用是:Test2 是一个空的类,Test1 里面有一个 Test2 的静态成员。Test 是程序的主入口,在一个无限循环内部,不断地尝试去实例化 Test1,并且在加载 Test1 出现异常的时候,尝试加载一下 Test2。
实验的步骤是:
- 编译以上类,运行
javac Test.java
- 将生成出的 Test2.class 重命名成 Test2.class.bak
- 运行
java Test
,这个时候程序去加载 Test1 的时候,就会出现NoClassDefFoundError
,并且在尝试加载 Test2 的时候,会出现ClassNotFoundException
。 - 将第二步重命名的 Test2.class.bak 该回成 Test2.class,这个时候程序去加载 Test1 的时候,就会出现
NoClassDefFoundError
,在加载 Test2 的时候,不会出现ClassNotFoundException
。
实验的第二步的目的是为了程序在加载 Test1 的时候因为找不到 Test2 出现 NoClassDefFoundError
,第四步是为了和第二步做对照,说明在后续程序可以加载到 Test2 的时候,在实例化 Test1 的时候,依旧出现 NoClassDefFoundError
在我的机器上,按照上面的方式去操作,结果如下:
结果正如预期,即使在后面 Test2 在 ClassPath 下的时候,NoClassDefFoundError
依旧出现,所以 JVM 里面肯定有地方对 NoClassDefFoundError
做了缓存。
JVM 里面的实现
带着这个疑问,请教了部门里面的 JVM 专家,这个猜测得到了证实,并且他给出了 JVM 内部具体处理这段逻辑的代码,处理的代码在 JDK 的 instanceKlass.cpp
这个文件里面:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
并且在 instanceClass.hpp
这个文件中,定义了类的 _init_state
,其中,is_in_error_state
这个方法的定义如下:
1
|
|