摘要:于是怀疑是不是对一个类的做了缓存,在第一次加载这个类出现以后,后续再尝试加载就直接抛出。上述类的的作用是是一个空的类,里面有一个的静态成员。
问题
今天在排查一个线上的问题,线上的一个应用在初始化一个类的静态字段的时候出现了 NoClassDefFoundError,并且在导致 NoClassDefFoundError 出现的根本原因消失后,后续再次尝试初始化这个类的时候,持续出现了 NoClassDefFoundError。
于是怀疑 JVM 是不是对一个类的 NoClassDefFoundError 做了缓存,在第一次加载这个类出现 NoClassDefFoundError 以后,后续再尝试加载就直接抛出 NoClassDefFoundError。
实验为了证实自己的猜想,尝试设计了一个简单的实验,一个涉及三个类
public class Test1 { static Test2 test2 = new Test2(); }
public class Test2 { }
public class Test { public static void main(String... args) throws Exception { while(true) { System.out.println("================================"); try { new Test1(); // 尝试实例化 Test1,触发 NoClassDefFoundError } catch (Throwable e) { e.printStackTrace(); try { Test.class.getClassLoader().loadClass("Test2"); // 尝试加载 Test2,用于证实当将 // Test2.class 拷贝到 ClassPath 下的时候, // Test2 就可以加载到了。 } catch (Throwable ex) { ex.printStackTrace(); } } Thread.sleep(3000); } } }
上述类的的作用是: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 这个文件里面:
bool instanceKlass::link_class_impl( instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) { // check for error state if (this_oop->is_in_error_state()) { ResourceMark rm(THREAD); THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(), this_oop->external_name(), false); } // return if already verified if (this_oop->is_linked()) { return true; }
并且在 instanceClass.hpp 这个文件中,定义了类的 _init_state,其中,is_in_error_state 这个方法的定义如下:
bool is_in_error_state() const { return _init_state == initialization_error; }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64242.html
摘要:看到的只是,而由泛型附加的类型信息对来说是不可见的。然后再加载执行类的静态变量以及静态语句块。接口中基本数据类型为而抽类象不是的。本地方法接口主要是调用或实现的本地方法及返回结果。用户自定义类加载器,在程序运行期间,通过的子类动态加载。 编译机制 编译主要是把 .Java文件转换为 .class 文件。其中转换后的 .class 文件就包含了元数据,方法信息等一些信息。比如说元数据就...
摘要:出现错误引发崩溃日志的流程分析这个错误是应用的方法总数限制造成的。 目录介绍 1.1 java.lang.ClassNotFoundException类找不到异常 1.2 java.util.concurrent.TimeoutException连接超时崩溃 1.3 java.lang.NumberFormatException格式转化错误 1.4 java.lang.Illegal...
摘要:可实现单例模式代码块初始化静态变量,只被执行一次内部类不能与外部类重名,只能访问外部类静态数据包括私有多分支选择整型或字符类型变量或整数表达式开始支持。 前言 大学期间接触 Java 的时间也不短了,不论学习还是实习,都让我发觉基础的重要性。互联网发展太快了,各种框架各种技术更新迭代的速度非常快,可能你刚好掌握了一门技术的应用,它却已经走在淘汰的边缘了。 而学习新技术总要付出一定的时间...
摘要:方案一设置运行环境变量运行后会把文件生成在目录下动态代理时生成文件缺点是只适用于动态代理方案二使用可以出中所有已加载的。 由于动态代理生成的class是直接以二进制的方式加载进内存中的,并没有对应的.class文件生成,所以如果想通过反编译工具查看动态代理生成的代码需要通过特殊的手段来处理。 方案一 设置运行环境变量,运行后会把class文件生成在classpath目录下 //动态代理...
摘要:但是将导入工程后,在使用时会出现等错误消息。初步认为是与自带的冲突。再运行工程,这个错误不再出现了,奇迹般的没问题了。我的理解是这样,不知道对不对,欢迎大侠指正。工程中下默认的是,而则应该是。所以按照该文的解决方法,更改就好了。 JSON解析库有很多,诸如Jackson,Json-lib,org.json,Gson和fastjson等,但是fastjson以其解析速度最快而脱颖而出。详...
阅读 1813·2021-11-11 16:55
阅读 731·2019-08-30 15:53
阅读 3575·2019-08-30 15:45
阅读 617·2019-08-30 14:10
阅读 3241·2019-08-30 12:46
阅读 2107·2019-08-29 13:15
阅读 2006·2019-08-26 13:48
阅读 910·2019-08-26 12:23