【标题描述】bisheng-jdk1.8.0_352中ClassLoader没有任何引用的情况无法gc清除,导致Class.forName加载到未清除的ClassLoader加载的Class信息,在oraclejdk8-192没有此问题
【环境信息】
软件信息:
oraclejdk8-192、bisheng-jdk1.8.0_352
【附件用例介绍】
public class A {
private B b = new B();
public void clear() {
this.b = null;
}
}
public class B {}
AppClassLoader
/ \
MyClassLoader(引用ModuleLoader(b.jar)去加载) ---- ModuleLoader(b.jar)
|
TestLoader(a.jar)
【问题复现步骤】
问题1. 用例中执行System.gc,则jdk8u-192和bisheng-jdk1.8.0_352在加载类的表现不同,在jdk8u-192当中:
Class.forName("B", false, MyClassLoader)返回b2Loader加载的B.class
MyClassLoader.loadClass("B")返回b2Loader加载的B.class
两者相同
在bisheng-jdk1.8.0_352中:
Class.forName("B", false, MyClassLoader)返回b1Loader加载的B.class
MyClassLoader.loadClass("B")返回b2Loader加载的B.class
两者不相同
问题2. 用例中不执行System.gc,则jdk8u-192和bisheng-jdk1.8.0_352在加载类的表现一样:
Class.forName("B", false, MyClassLoader)返回b1Loader加载的B.class
MyClassLoader.loadClass("B")返回b2Loader加载的B.class
两者不相同
【预期结果】
在oraclejdk8u-192和bisheng-jdk1.8.0_352(高于192版本即可)运行mvn clean test,testLoadAfterGC用例都会成功
【实际结果】
在oraclejdk8u-192和bisheng-jdk1.8.0_352(高于192版本即可)运行mvn clean test, testLoadAfterGC在bisheng-jdk1.8.0_352会失败
【目前分析】
jvm有几个关键类ClassLoaderData.cpp, SystemDictionary.cpp
每个类加载器都会对应一个 ClassLoaderData 的数据结构,里面会存譬如具体的类加载器对象,加载的 klass,这里关注的属性:
SystemDictionary类的定义在classfile/systemDictionary.hpp中,是一个系统字典类,用于保存所有已经加载完成的类,
通过一个支持自动扩容的HashMap保存,key是表示类名Symbol指针和对应的类加载器oop指针,value是对应的Klass指针,
当一个新的类加载完成后就会在SystemDictionary中添加一个新的键值对,关键属性:
Class.forName("B", false, MyClassLoader)加载过程中,SystemDictionary.resolve_instance_class_or_null方法会先从dictionary字典中查找要加载的类, 此处直接找到后返回Klass, 不会再委托给MyClassLoader加载,但是按道理执行gc之后ModuleLoader已经被清除了,没有任何强引用。对与Klass来说,有initialloader和defineloader两个概念,这个案例中B.class的initialloader是MyClassLoader,defineloader就是ModuleLoader,gc时会将ModuleLoader对应ClassLoaderData中unloading设置为true,最后清理SystemDictionary字典中对应DictionaryEntry。清理逻辑大致如下:
此案例中B.class对应loader_data为MyClassLoader, define_loader_data为ModuleLoader,gc时ModuleLoader会设置unloading=true
但是MyClassLoader不会,结合上面提交记录,将define_loader_data的判断逻辑删除了,因此DictionaryEntry并没有删除。
Hi osinfra, welcome to the openEuler Community.
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here.
If you have any questions, please contact the SIG: Compiler, and any of the maintainers: @Noah , @eastb233 , @Peilin Guo , @guoge , @cf-zhao , @jiangfeilong , @编译小伙 , @周磊 , @wangyadong , @stubCode , @kuen
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
感谢反馈,确实是改patch造成的差异(https://github.com/adoptium/jdk8u//commit/4b9ec1c4fa735e97caea5bd73555ce131be60ca5),由于这个patch是一个安全类的修复,所以我们看不到修改的原因。
我在本地环境也做了一些验证,发现Oracle JDK新版本以及JDK主线都有描述的问题,当前来看191之后的所有发型版都存在这个现象。
从issue中的例子来看,gc之后ModuleLoader没有被回收掉,可通过如下例子发现:
$ git clone git clone https://gitee.com/openeuler/bishengjdk-8
$ cd bishengjdk-8 && bash configure --with-debug-level=slowdebug && make images
$ build/linux-x86_64-normal-server-slowdebug/images/j2sdk-image/bin/java -cp ./target/classes/ -XX:+TraceClassLoaderData com.bingli.MyTest true
[ClassLoaderData: create class loader data 0x00007efda840be40 for instance 0x0000000580157058 of sun/misc/Launcher$AppClassLoader]
*********************************************
b1 classloader: 1829164700
[ClassLoaderData: create class loader data 0x00007efda8430750 for instance 0x0000000580104528 of com/bingli/TestLoader]
[ClassLoaderData: create class loader data 0x00007efda8430ad0 for instance 0x000000008020e458 of com/bingli/MyClassLoader]
[ClassLoaderData: create class loader data 0x00007efda8430d30 for instance 0x0000000580100f20 of com/bingli/ModuleLoader]
[ClassLoaderData: unload loader data 0x00007efda8430750 for instance 0x00000005a56980f0 of com/bingli/TestLoader]
b2 classloader: 685325104
Class.forName load B class, the classloader is : 1829164700
[ClassLoaderData: create class loader data 0x00007efda84307f0 for instance 0x0000000580100180 of com/bingli/ModuleLoader]
ClassLoader.loadClass load B class, the classloader is : 685325104
*********************************************
可以发现只有TestLoader被unload了。
在触发GC前执行MyClassLoader.instance = null后,打印日志如下:
[ClassLoaderData: create class loader data 0x00007f515c403990 for instance 0x0000000580157058 of sun/misc/Launcher$AppClassLoader]
*********************************************
b1 classloader: 1829164700
[ClassLoaderData: create class loader data 0x00007f515c427c30 for instance 0x0000000580104528 of com/bingli/TestLoader]
[ClassLoaderData: create class loader data 0x00007f515c4284b0 for instance 0x00000000802191d0 of com/bingli/MyClassLoader]
[ClassLoaderData: create class loader data 0x00007f515c428710 for instance 0x0000000580100f20 of com/bingli/ModuleLoader]
[ClassLoaderData: unload loader data 0x00007f515c428710 for instance 0x00000005a5698000 of com/bingli/ModuleLoader]
[ClassLoaderData: unload loader data 0x00007f515c4284b0 for instance 0x00000000802191d0 of com/bingli/MyClassLoader]
[ClassLoaderData: unload loader data 0x00007f515c427c30 for instance 0x00000005a56980f0 of com/bingli/TestLoader]
b2 classloader: 685325104
[ClassLoaderData: create class loader data 0x00007f515c4283e0 for instance 0x0000000580100778 of com/bingli/MyClassLoader]
[ClassLoaderData: create class loader data 0x00007f515c4327c0 for instance 0x0000000580100180 of com/bingli/ModuleLoader]
Class.forName load B class, the classloader is : 685325104
ClassLoader.loadClass load B class, the classloader is : 685325104
*********************************************
可以发现这时才真正回收掉了ModuleLoader,所以新版本在GC之后引用旧的classloader看起来没有问题
登录 后才可以发表评论