JVM类加载过程

类加载的概念

JVM把.class文件的数据加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制。

类加载的过程

Java虚拟机中类加载的全过程,即加载、验证、准备、解析和初始化这五个阶段;

类的个生命周期如下:

在这里插入图片描述

1,加载阶段(引入)

通过类加载器:

1)通过一个类的全限定名来获取定义此类的二进制字节流。

2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2,验证阶段(校验)

验证是连接阶段的第一步,目的是确保Class文件的字节流中包含的信息被当作代码运行后不会危害虚拟机自身的安全。

Class文件并不一定只能由Java源码编译而来,它可以使用包括靠键盘0和1直接在二进制编辑器中敲出 Class文件在内的任何途径产生。上述Java代码无法做到的事情在字节码层面上都是可以实现的,至少语义上是可以表达出来的。Java虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有错误或有恶意企图的字节码流而导致整个系统受攻击甚至崩溃,所以验证字节码是Java虚拟机保护自身的一项必要措施。

3,准备阶段(机器维度初始化)

为类中定义的变量(即静态变量,被static修饰的变量,仅包括类变量)分配内存并设置类变量初始值。初始值通常情况下是数据类型的零值。

在这里插入图片描述

4,解析(符号转指针)

虚拟机将常量池内的符号引用(符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。)替换为直接引用(直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。)的过程。
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符 7 类符号引用进行。

5,初始化(源码维度初始化)

类的初始化阶段是类加载过程的最后一个步骤。根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。

初始化阶段就是执行类构造器()方法的过程。()并不是程序员在Java代码中直接编写的方法,它是Javac编译器的自动生成物。

类加载器

概念:

类加载阶段中的 –1,加载阶段

“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”。

双亲委派模型:

站在Java虚拟机的角度来看,只存在两种不同的类加载器:

一种是启动类加载器(BootstrapClassLoader),是虚拟机自身的一部分;

另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader。

启动类加载器(Bootstrap Class Loader):

这个类加载器负责加载存放在 <JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名识别,如rt .jar、t ools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中

扩展类加载器(Extension Class Loader):

这个类加载器是在类sun.misc.Launcher$ExtClassLoader 中以Java代码的形式实现的。它负责加载<JAVA_HOM E>\lib\ext目录中,或者被java.ext.dirs系统变量所 指定的路径中所有的类库。

应用程序类加载器(Application Class Loader):

这个类加载器由sun.misc.Launcher$AppClassLoader来实现。也称它为“系统类加载器”。它负责加载用户类路径 (ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

在这里插入图片描述

双亲委派模型的工作过程是:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

好处:

Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系,保证Java程序的稳定。

前提:

比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

这里所指的“相等”,包括代表类的Class对象的equals()方 法 、isAssignableFrom()方法、isInstance()方法的返回结果,也包括了使用instanceof关键字做对象所属关系判定等各种情况。

如类java.lang. Object ,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果我们自己也编写了一个名为java.lang.Object的类,并放在程序的 ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应 用程序将会变得一片混乱。

源码:

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
	// 首先,检查请求的类是否已经被加载过了 Class c =findLoadedClass(name); if (c == null) {
	try {
		if (parent != null) {
			c = parent.loadClass(name, false);
		} else {
			c = findBootstrapClassOrNull(name); }
		} catch (ClassNotFoundException e) {
		// 如果父类加载器抛出ClassNotFoundException // 说明父类加载器无法完成加载请求
		}
  
	if (c == null) {
		// 在父类加载器无法加载时
		// 再调用本身的findClass方法来进行类加载 c = findClass(name);
	} 

	if (resolve) { 
  		resolveClass(c);
	}
  
	 return c; 
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值