Java类加载和卸载是Java虚拟机(JVM)在运行时管理类的生命周期的两个重要过程。这两个过程由JVM的类加载器子系统负责,是Java动态性的基础之一。
类加载
类加载是指将类的.class文件中的二进制数据读入到内存中,并将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
类加载的主要步骤包括:
加载:通过类的全名获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,最后在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
链接:包括验证、准备和解析三个阶段。验证是为了确保被加载的类信息符合JVM规范,无安全方面的问题;准备是为类的静态变量分配内存,并将其初始化为默认的初始值;解析是将常量池内的符号引用替换为直接引用的过程。
初始化:执行类构造器方法()的过程。此方法由编译期自动收集类中的所有类变量的赋值动作和静态代码块(static{})中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。虚拟机会保证一个类的()方法在多线程环境中被正确地加锁和同步。
双亲委派加载机制
Java双亲委派加载机制是Java虚拟机(JVM)在加载类时采用的一种重要机制,它保证了Java程序的安全性和稳定性。
工作原理
双亲委派加载机制的核心思想是在加载类时,首先由最顶层的类加载器(通常是启动类加载器)尝试加载,如果无法加载,则交给下一级的类加载器去加载,直到自己尝试加载为止。这种机制形成了一个层次化的类加载器结构,每个类加载器都有一个父类加载器(注意这里的父子关系并非继承关系,而是使用组合关系来复用父加载器的代码)。
具体来说,当一个类加载器收到类加载请求时,它会按照以下步骤进行工作:
检查这个类是否已经被加载过,如果已经被加载过,则直接返回对应的Class对象。
如果没有被加载过,则首先将这个请求委派给父类加载器去完成,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。
如果父类加载器无法完成这个加载请求(比如找不到所需的类),则子类加载器会尝试自己去加载这个类。
涉及的算法
双亲委派加载机制主要涉及的是类的加载算法。这个算法可以概括为“自底向上检查,自顶向下加载”。具体来说:
自底向上检查:当应用程序需要加载某个类时,首先从最底层的类加载器开始检查是否已经加载过这个类。如果没有,则逐层向上检查,直到到达最顶层的启动类加载器。
自顶向下加载:如果在自底向上的检查过程中发现某个类加载器已经加载过这个类,则直接使用这个已经加载的类。否则,从最顶层的启动类加载器开始尝试加载这个类。如果顶层加载器无法加载,则逐层向下尝试,直到某个类加载器能够成功加载这个类为止。
优缺点
双亲委派加载机制的优点主要包括:
安全性:通过双亲委派的方式,可以确保Java核心API的稳定性和安全性。因为核心API类(如java.lang包中的类)都是由启动类加载器加载的,它们不会被恶意代码篡改或替换。
避免类重复加载:由于每个类加载器在加载类之前都会先检查父类加载器是否已经加载过这个类,所以可以避免类的重复加载,提高了资源利用效率。
然而,双亲委派加载机制也存在一些缺点:
灵活性受限:由于双亲委派机制是严格按照层次结构进行类加载的,所以在某些情况下可能会限制类的加载灵活性。例如,在某些复杂的模块化系统中,可能需要自定义的类加载器来加载特定模块的类,这时双亲委派机制可能会成为障碍。
性能开销:虽然双亲委派机制可以避免类的重复加载,但在某些情况下也可能带来额外的性能开销。例如,当一个类加载器收到一个类加载请求时,它需要逐层向上检查父类加载器是否已经加载过这个类,这个检查过程可能会消耗一定的时间。
总的来说,双亲委派加载机制是Java虚拟机中一种重要的类加载机制,它保证了Java程序的安全性和稳定性。虽然它存在一些缺点,但在大多数情况下仍然是Java类加载的首选方案。
类卸载
类卸载是指当类不再被使用时,将其从JVM中移除以释放内存空间的过程。类卸载的条件比较苛刻,只有当一个类不再被任何引用所指向时,即该类所有的实例都已被回收,并且加载该类的ClassLoader也已经被回收,该类才可能被卸载。在JVM中,类卸载不是强制的,而是由JVM的垃圾回收器来决定的。如果某个类长时间没有被使用,那么JVM可能会将其标记为可回收的,并在后续的垃圾回收过程中将其卸载。
类卸载的过程相对简单,主要包括以下几个步骤:
判断类是否不再被使用:这是类卸载的前提条件。如果一个类仍然被其他类或对象所引用,那么它就不能被卸载。
卸载类:如果确定一个类不再被使用,那么JVM就会开始卸载它。卸载过程包括释放该类在方法区占用的内存空间,以及相关的其他资源。
回收ClassLoader:如果某个ClassLoader已经没有任何活动类(即它加载的所有类都已经被卸载),那么它自身也可能被垃圾回收器回收。
需要注意的是,类卸载并不总是发生的。在某些情况下,尽管某个类已经不再被使用,但由于某些原因(如存在对该类的静态引用、该类的ClassLoader仍然存活等),它可能仍然保留在JVM中,不会被卸载。因此,开发者在编写代码时应尽量避免创建无用的类或对象,以减少内存占用和提高程序的运行效率。
领取专属 10元无门槛券
私享最新 技术干货