目录

类的生命周期

引用关系

加载器和Class对象

类、类的Class对象、类的实例对象

类的卸载

具体例子

总结


类的生命周期

当Sample类被加载、连接和初始化后,它的生命周期就开始了。

当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就会结束生命周期,Sample类在方法区内的数据也会被卸载,从而结束Sample类的生命周期。

由此可见,一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期

引用关系

加载器和Class对象

在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。

另一方面,一个Class对象总是会引用它的类加载器。调用Class对象的getClassLoader()方法,就能获得它的类加载器。

由此可见,Class实例和加载它的加载器之间为双向关联关系

类、类的Class对象、类的实例对象

一个类的实例总是引用代表这个类的Class对象。

在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。

此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。

类的卸载

由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。

前面介绍过,Java虚拟机自带的类加载器包括根类加载器扩展类加载器系统类加载器

Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的

由用户自定义的类加载器加载的类是可以被卸载的。

具体例子


Class clazz=loader.loadClass("Sample"); Object obj=clazz.newInstance();


java loadAgent卸载_后端

loader1变量和obj变量间接引用代表Sample类的Class对象,而objClass变量则直接引用它。

如果程序运行过程中,将上图左侧三个引用变量都置为null,此时Sample对象结束生命周期,MyClassLoader对象结束生命周期,代表Sample类的Class对象也结束生命周期,Sample类在方法区内的二进制数据被卸载

当再次有需要时,会检查Sample类的Class对象是否存在,如果存在会直接使用,不再重新加载;如果不存在Sample类会被重新加载,在Java虚拟机的堆区会生成一个新的代表Sample类的Class实例(可以通过哈希码查看是否是同一个实例)。

总结

卸载类需要满足 3 个要求:

1 该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。

2 该类没有在其他任何地方被引用

3 该类的类加载器的实例已被 GC

所以,在 JVM 生命周期内,由 jvm 自带的类加载器 BootstrapClassLoader, ExtClassLoader, AppClassLoader 加载的类是不会被卸载的,所以它们(类加载器的实例)肯定不会被回收。但是由我们自定义的类加载器加载的类是可能被卸载的。

(1) 启动类加载器加载的类型在整个运行期间是不可能被卸载的(jvm和jls规范);

(2) 被系统类加载器和标准扩展类加载器加载的类型在运行期间不太可能被卸载,因为系统类加载器实例或者标准扩展类的实例基本上在整个运行期间总能直接或者间接的访问的到,其达到unreachable的可能性极小。(当然,在虚拟机快退出的时候可以,因为不管ClassLoader实例或者Class(java.lang.Class)实例也都是在堆中存在,同样遵循垃圾收集的规则);

(3) 被开发者自定义的类加载器实例加载的类型只有在很简单的上下文环境中才能被卸载,而且一般还要借助于强制调用虚拟机的垃圾收集功能才可以做到.可以预想,稍微复杂点的应用场景中(尤其很多时候,用户在开发自定义类加载器实例的时候采用缓存的策略以提高系统性能),被加载的类型在运行期间也是几乎不太可能被卸载的(至少卸载的时间是不确定的)

综合以上三点, 一个已经加载的类型被卸载的几率很小至少被卸载的时间是不确定的。同时我们可以看的出来,开发者在开发代码时候,不应该对虚拟机的类型卸载做任何假设的前提下来实现系统中的特定功能。