一文读懂Java中类的卸载机制
一文读懂Java中类的卸载机制
先下结论:使用JVM自带的类加载器(根加载器、扩展类加载器、系统类加载器)加载的类永远不会被卸载,使用自己创建的类加载器才会可能被卸载。
至于为什么接下来就来一起研究吧。
当一个类被加载、连接和初始化后,它的生命周期就开始了。
当代表某个类的Class对象不再被引用,即不可达时,Class对象就会结束生命周期,该类在方法区内的数据也会被卸载,从而结束该类的生命周期。
由此可见,一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。
那么Class对象什么时候不可达呢?这就需要分析Class对象的引用了,如果对该Class对象的引用一直存在,那么他自然就不会被回收,该Class所对应的类也就自然不会被卸载。
Class对象和类加载器
在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。
另一方面,一个Class对象总是会引用它的类加载器。
通过调用Class对象的getClassLoader()方法,就能获得它的类加载器。
由此可见,Class实例和加载它的加载器之间为双向关联关系。
Class对象和类以及类的实例对象
一个类的实例总是引用代表这个类的Class对象。
在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。
此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。
文章一开头就下过结论:
由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。
Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。
因为Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的。前面已经说过当某个类代表的Class对象被回收的时候,这个类才会被卸载。因为该类的Class对象一直被三种类加载器引用,所以这个类在JVM运行过程中永远不会被卸载。
由用户自定义的类加载器加载的类是可以被卸载的。
以一个Sample类举例子说明,其中MyClassLoader类是自定义的类加载器。
其中:
loader1变量直接引用我们编写的类加载器;
objClass变量直接引用我们的Sample类对应的Class对象;
obj变量直接引用Sample类的一个对象实例。
并且由于类加载器内部引用了他所加载的类对应的Class对象,以及Sample类的实例对象引用了该类对应的Class对象,所以loader1变量和obj变量间接引用代表Sample类的Class对象,而objClass变量则直接引用它。
如果程序运行过程中,将上图左侧三个引用变量都置为null,此时没有任何变量引用Sample类对应的Class对象,所以Sample对应的Class对象生命周期结束,即Sample类在方法区内的二进制数据被卸载。
当再次有需要时,会检查Sample类的Class对象是否存在,如果存在会直接使用,不再重新加载;如果不存在Sample类会被重新加载,在Java虚拟机的堆区会生成一个新的代表Sample类的Class实例(可以通过hashcode查看是否是同一个实例)。
启动类加载器加载的类型在整个运行期间是不可能被卸载的;
被系统类加载器和标准扩展类加载器加载的类型在运行期间不太可能被卸载,因为系统类加载器实例或者标准扩展类的实例基本上在整个运行期间总能直接或者间接的访问的到,其不可达的可能性极小。
被开发者自定义的类加载器实例加载的类型只有在很简单的上下文环境中才能被卸载,而且一般还要借助于强制调用虚拟机的垃圾收集功能才可以做到.可以预想,稍微复杂点的应用场景中(尤其很多时候,用户在开发自定义类加载器实例的时候采用缓存的策略以提高系统性能),被加载的类型在运行期间也是几乎不太可能被卸载的(至少卸载的时间是不确定的)
综合以上三点, 一个已经加载的类型被卸载的几率很小至少被卸载的时间是不确定的。
一文读懂Java中类的卸载机制相关教程