JVM类加载机制

1 类加载过程(类加载生命周期)

在这里插入图片描述
JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化。
上图中解析和初始化的位置是可以互换的,一旦解析在初始化之后开始,这就是我们经常所说的“动态绑定”。
除此之外,这些阶段通常都是互相交叉的混合式进行,各个阶段只保证按部就班的开始,并不保证按部就班的进行或完成。

1.1 加载

  • 通过一个类的全限定名来获取定义此类的二进制字节流。
  • 将获取到的二进制字节流转化成一种数据结构并放进方法区。
  • 在内存中生成一个代表此类的java.lang.Class对象,作为访问方法区中各种数据的接口。

加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个 Class 文件获取,这里既可以从 ZIP 包中读取(比如从 jar 包和 war 包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将 JSP 文件转换成对应的 Class 类)。

1.2 连接

1.2.1 验证

这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

1.2.2 准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。注意这里所说的初始值概念,比如int的零值为0,long为0L,boolean为false… …真正的初始化赋值是在初始化阶段进行的。
假设一个类变量定义为:
public static int v = 8080;实际上变量 v 在准备阶段过后的初始值为 0 而不是 8080,将 v 赋值为 8080 的 put static 指令是程序被编译后,存放于类构造器方法之中;
但是注意如果声明为:
public static final int v = 8080;在编译阶段会为 v 生成 ConstantValue 属性,在准备阶段虚拟机会根据 ConstantValue 属性将 v
赋值为 8080。

1.2.3 解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。

1.3 初始化

始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由 JVM 主导。到了初始阶段,才开始真正执行类中定义的 Java 程序代码。

1.3.1 初始化时机

  1. 创建类的实例,也就是new一个对象。
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值。
  3. 调用类的静态方法。
  4. 反射(Class.forName(“com.lyj.load”))。
  5. 初始化一个类的子类(会首先初始化子类的父类)。
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类。

1.3.2 类构造器

初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类的方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法。
注意以下几种情况不会执行类初始化:

  1. 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  2. 定义对象数组,不会触发该类的初始化。
  3. 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
  4. 通过类名获取 Class 对象,不会触发类的初始化。
  5. 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
  6. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。

2 类加载器

上面说了那么多,类加载器就是用于实现类加载(1.1小节)动作的一段代码实现。
“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块成为“类加载器”。JVM 提供了 3 种类加载器:

2.1 启动类加载器(Bootstrap ClassLoader)

也叫根类加载器,用来加载 Java 的核心类,负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar)的类。

2.2 扩展类加载器(Extension ClassLoader)

用来加载JRE的扩展目录,负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库。

2.3 应用程序类加载器(Application ClassLoader)

也叫系统类加载器,负责加载用户路径(classpath)上的类库。

2.4 自定义类加载器

也可以通过继承 java.lang.ClassLoader实现自定义的类加载器。

类加载器的命名空间:对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类命名空间。也就是说,你现在要比较两个类是否相等,只有在这两个类是同一个类加载器加载的前提下才有意义。

2.5 双亲委派模型

上面讨论的应用类加载器加载类的模式就是我们常说的双亲委派模型。所谓的双亲委派,当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

2.5.1 类加载器加载Class大致要经过如下8个步骤

  1. 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
  2. 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
  3. 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
  4. 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
  5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
  6. 从文件中载入Class,成功后跳至第8步。
  7. 抛出ClassNotFountException异常。
  8. 返回对应的java.lang.Class对象。
    在这里插入图片描述

双亲委派的优点

  1. 安全,可避免用户自己编写的类动态替换Java的核心类,比如有人想替换系统级别的类:String.java。篡改它的实现,但是在这种机制下这些系统的类已经被Bootstrap classLoader加载过了,所以并不会再去加载,从一定程度上防止了危险代码的植入;
  2. 避免全限定命名的类重复加载(使用了findLoadClass()判断当前类是否已加载。

3 归纳:编译、解释、运行

在这里插入图片描述

3.1 编译器

将Java源文件(.java文件)编译成字节码文件(.class文件,是特殊的二进制文件,二进制字节码文件)。javac.exe可以简单看成是Java编译器。

3.2 解释器

运行JVM字节码的工作是由解释器来完成的。解释执行过程分三部进行:代码的装入、代码的校验和代码的执行。装入代码的工作由"类装载器"(class loader)完成。类装载器负责装入运行一个程序需要的所有代码,这也包括程序代码中的类所继承的类和被其调用的类。当类装载器装入一个类时,该类被放在自己的名字空间中。除了通过符号引用自己名字空间以外的类,类之间没有其他办法可以影响其他类。在本台计算机上的所有类都在同一地址空间内,而所有从外部引进的类,都有一个自己独立的名字空间。这使得本地类通过共享相同的名字空间获得较高的运行效率,同时又保证它们与从外部引进的类不会相互影响。当装入了运行程序需要的所有类后,解释器便可确定整个可执行程序的内存布局。解释器为符号引用同特定的地址空间建立对应关系及查询表。通过在这一阶段确定代码的内存布局,Java很好地解决了由超类改变而使子类崩溃的问题,同时也防止了代码对地址的非法访问。随后,被装入的代码由字节码校验器进行检查。校验器可发现操作数栈溢出,非法数据类型转化等多种错误。通过校验后,代码便开始执行了,本步骤由执行引擎Execute Engine来完成。执行引擎把字节码转为机器码,然后操作系统才可以真正调用,在硬件环境上执行代码。执行引擎通过Java字节码解释器(一行一行解释字节码)和JIT(Just In Time)即时编译器(对热代码整段编译)来完成机器码的翻译工作。

注:以上内容来源网络收集和归纳,如有错误请不吝赐教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值