JVM-(3)Class 加载过程

Class 加载过程

承接上篇 Class 文件格式

  • loading : 指.class文件加载到内存的过程
  • verfication : 核实.class文件是否符合JVM规范
  • preparation:给class静态变量赋默认值
  • initializing:静态变量赋初始值

类加载器

JVM是通过类加载器(ClassLoader)加载 class 文件到内存的,包括以下两个步骤:

  • 开辟内存区域存放.class二进制文件
  • 生成一个Class类对象,这个Class对象指向了二进制文件


Java 中有不同类型的 ClassLoader ,它们负责加载不同类库的 class 文件,如上图

  • BootstrapClassLoader
  • ExtClassLoader
  • AppClassLoader

看下以下例子:

public class ClassloaderTest {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        System.out.println(sun.awt.HKSCS.class.getClassLoader());
        System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
        System.out.println(ClassloaderTest.class.getClassLoader());
    }
}

执行结果:

null
null
sun.misc.Launcher$ExtClassLoader@5a07e868
sun.misc.Launcher$AppClassLoader@18b4aac2

String 处于 rt.jar 包下,HKSCS处于 charsets.jar 包下,它们都是由 BootstrapClassLoader 加载的,而BootstrapClassLoader 由c++ 实现,java 代码中并没有这个类,所以输出为null

ClassloaderTest 为自定义类,由 AppClassLoader 加载

类加载器关系

CustomClassLoader 父加载器为 AppClassLoader
AppClassLoader 父加载器为 ExtClassLoader
ExtClassLoader 父加载器为 BootstrapClassLoader

注意: AppClassLoader 不是继承自 ExtClassLoader ,而是 AppClassLoader 类中有一个 private final ClassLoader parent; 属性,它指向了ExtClassLoader,AppClassLoader、ExtClassLoader 都是继承自URLClassLoader

以下例子可以说明这一点:

public class ClassloaderTest2 {
    public static void main(String[] args) {
        System.out.println(ClassloaderTest2.class.getClassLoader());
        System.out.println(ClassloaderTest2.class.getClassLoader().getParent());
        System.out.println(ClassloaderTest2.class.getClassLoader().getParent().getParent());
    }
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@7440e464
null

Class 文件加载过程

如下图,当使用自定义类加载器CustomClassLoader 加载class文件时,步骤如下:

  1. 首先在CustomClassLoader 缓存中查找,缓存中有直接返回结果,没有交给 AppClassLoader 处理
  2. AppClassLoader 同样在自己的缓存中查找是否已经加载,有则直接返回,没有就交给 ExtClassLoader 处理
  3. ExtClassLoader 同样在自己缓存中查找,有则返回,没有就交给BootstrapClassLoader处理
  4. BootstrapClassLoader 在自己缓存中查找,有则返回,没有则交给ExtClassLoader 处理
  5. ExtClassLoader 调用 自己的 findClass 方法,假设当前要加载的类为DNSNameService那么就加载成功了,否则,交给 AppClassLoader 处理
  6. AppClassLoader 调用自己的 findClass 方法,假设当前要加载的类为classpath路径下的class则加载成功,否则交给自定义加载器 CustomClassLoader 处理
  7. CustomClassLoader 调用实现的 findClass 方法

这样一个递归查找的过程称为 双亲委派机制

为什么使用双亲委派机制

主要出于安全考虑,例如假设有一个名为 java.lang.String 的自定义String类可以通过自定义的类加载器加载,从而达到替换官方string 的目的,这样就造成了安全问题。
而有了双亲委派机制,那么就首先会从父加载器中查找是否已经加载了名为 java.lang.String 的 class 文件,有则直接返回,不会走到自定义加载器

解释器与编译器

解释 “解释器” 和 “编译器” 前先要了解什么是解释执行,什么是编译执行:

  • 解释执行:将.class 字节码文件逐句翻译成对应平台的机器指令并执行,不会产生机器码文件
  • 编译执行:将.class文件编译成机器码后再执行

优缺点:

  • 启动效率:解释执行不用编译,所以启动效率更高
  • 运行效率:编译执行只需要编译一次,而解释执行每次都要经过编译,所以编译执行的运行效率更高

虚拟机执行模式

在部分的商用虚拟机(Sun HotSpot、IBM J9)中,Java程序最初是通过解释器 (Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把 这些代码认定为“热点代码”(Hot Spot Code)。为了提高热点代码的执行效率,在运行时, 虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个 任务的编译器称为即时编译器(Just In Time Compiler,下文中简称JIT编译器)。

(摘自深入理解Java虚拟机第2版

通过 java -version 命令可以查看虚拟机的执行模式,默认为 mixed mode 混合模式

C:\Users\Administrator>java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

为何使用混合模式

解释器与编译器两者各有优势:当 程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。 在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码 之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统 中),可以使用解释执行节约内存,反之可以使用编译执行来提升效率。

如何更改执行模式

使用参数“-Xint”强制虚拟机运行 于“解释模式”(Interpreted Mode),这时编译器完全不介入工作,全部代码都使用解释方式执行

使用参数“-Xcomp”强制虚拟机运行于“编译模式”(Compiled Mode), 这时将优先采用编译方式执行程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值