最近遇到一个Java异常,排查了半天,具体的异常详情如下:
java.lang.LinkageError: loader constraint violation: loader (instance of sun/misc/Launcher$AppClassLoader) previously initiated loading for a different type with name "grpc/cma/cimiss2/music/pb/common/RetGridArray2D"
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_172]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_172]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_172]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_172]
at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_172]
at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_172]
at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_172]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_172]
at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_172]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_172]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_172]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_172]
at grpc.cma.cimiss2.music.pb.nafp.NafpQueryServiceGrpc.getCallAPIToGridArray2DMethod(NafpQueryServiceGrpc.java:53) ~[grpc-client.jar:na]
这个错误几乎很少见到,是关于Java类加载器的一个异常,定位到的代码位置如下:
可以看到,在jar包grpc-client.jar中的类加载的类并不在同一个jar包下面,而是动态编译出来的。
spring boot使用了两个类加载器(classloader),对于不会变化的类,比如引入的第三方jar包中的类,加载到一个base classloader中,而开发者实际编写的类,被加载到另一个restart classloader中。当应用重启的时候,整个restart classloader被销毁后重建,而base classloader则保留下来不必再次加载。通过这种方式实现restart效率的提升,模拟了这种方式下的"热部署"。
所以grpc-client.jar包中需要的类RetGridArray2D是通过spring boot devtools的base classloader加载,而我们的应用中的RetGridArray2D类是通过restart classloader类加载器加载的,所以问题就是,同一个类由两个类加载器加载了,这两个类在Java虚拟机中是不同的,所以就会导致这个问题。
下面的博文可以参考一下:
LinkageError之loader (instance of xxx) previously initiated loading for a different type with name “lib/MyData”
【JVM】查看JVM加载的类及类加载器的方法
spring boot devtools使用和源码解析
解决办法就是把pom文件中devtools的依赖注释掉。