1、JVM之类加载器

1、什么是JVM

先来看下百度百科的解释:java

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是经过在实际的计算机上仿真模拟各类计算机功能来实现的。数据库

晦涩难懂有没有,简单理解就是说虚拟机是物理机的软件实现。编程

Java 的设计理念是 WORA(Write Once Run Anywhere,一次编写处处运行)。编译器将 Java 文件编译为 Java .class 文件,而后将 .class 文件输入到 JVM 中,JVM 执行类文件的加载和执行,最后转变成机器能够识别的机器码进行最终的操做。数组

JVM体系结构图缓存

2、类加载的过程

一、加载安全

加载指的是将类的class文件读入到内存,并为之建立一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。网络

类的加载由类加载器完成,类加载器一般由JVM提供,这些类加载器也是前面全部程序运行的基础,JVM提供的这些类加载器一般被称为系统类加载器。除此以外,开发者能够经过继承ClassLoader基类来建立本身的类加载器。数据结构

经过使用不一样的类加载器,能够从不一样来源加载类的二进制数据,一般有以下几种来源。jvm

  • 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式。
  • 从JAR包加载class文件,这种方式也是很常见的,前面介绍JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM能够从JAR文件中直接加载该class文件。
  • 经过网络加载class文件。
  • 把一个Java源文件动态编译,并执行加载。
  • 类加载器一般无须等到“首次使用”该类时才加载该类,Java虚拟机规范容许系统预先加载某些类。

2.连接
当类被加载以后,系统为之生成一个对应的Class对象,接着将会进入链接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为以下3个阶段。布局

1)验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其余类协调一致。Java是相对C++语言是安全的语言,例如它有C++不具备的数组越界的检查。这自己就是对自身安全的一种保护。验证阶段是Java很是重要的一个阶段,它会直接的保证应用是否会被恶意入侵的一道重要的防线,越是严谨的验证机制越安全。验证的目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

2)准备:类准备阶段负责为类的静态变量分配内存,并设置默认初始值。

3)解析:将类的二进制数据中的符号引用替换成直接引用。说明一下:符号引用:符号引用是以一组符号来描述所引用的目标,符号能够是任何的字面形式的字面量,只要不会出现冲突可以定位到就行。布局和内存无关。直接引用:是指向目标的指针,偏移量或者可以直接定位的句柄。该引用是和内存中的布局有关的,而且必定加载进来的。

3.初始化
初始化是为类的静态变量赋予正确的初始值,准备阶段和初始化阶段看似有点矛盾,实际上是不矛盾的,若是类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行连接的验证这一步骤,验证经过后准备阶段,给a分配内存,由于变量a是static的,因此此时a等于int类型的默认初始值0,即a=0,而后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

3、类加载器ClassLoader

负责加载class文件,class文件在文件开头由特定的文件标识,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构而且ClassLoader只负责class文件的加载,至于它是否能够运行,则由Execution Engine(执行引擎)决定。

四种类加载器

虚拟机自带的加载器

  • 启动类加载器(Bootstrap)C++编写

Bootstrap ClassLoader是由C/C++编写的,它自己是虚拟机的一部分,因此它并非一个JAVA类,也就是没法在java代码中获取它的引用,JVM启动时经过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,以前的int.class,String.class都是由它加载。而后呢,咱们前面已经分析了,JVM初始化sun.misc.Launcher并建立Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,可是它却能够做用一个ClassLoader的父加载器。好比ExtClassLoader。这也能够解释以前经过ExtClassLoader的getParent方法获取为Null的现象

System.out.println(new Object().getClass().getClassLoader());
//这也就是为何这段代码打印出结果为null的缘由
  • 扩展类加载器(Extension)Java编写

ExtClassLoader称为扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的全部jar包或者由java.ext.dirs系统属性指定的jar包.放入这个目录下的jar包对AppClassLoader加载器都是可见的(由于ExtClassLoader是AppClassLoader的父加载器,而且Java类加载器采用了委托机制).

  • 应用程序类加载器(AppClassLoader)也叫系统类加载器,加载当前应用的classpath的全部类

AppClassLoader应用类加载器,又称为系统类加载器,负责在JVM启动时,加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操做系统属性所指定的JAR类包和类路径.

public class AppClassLoaderTest {

    public static void main(String[] args) {
        ClassLoader classLoader = Test.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
    }

    private static class Test {

    }
}

//执行结果以下
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
//从上面的运行结果能够得知AppClassLoader的父加载器是ExtClassLoader

用户自定义的加载器

  • Java.lang.ClassLoader的子类,用户能够定制类的加载方式

4、类加载机制

JVM的类加载机制主要有以下3种:

  • 全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其余Class也将由该类加载器负责载入,除非显示使用另一个类加载器来载入。
  • 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器没法加载该类时才尝试从本身的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,若是父加载器能够完成类加载任务,就成功返回;只有父加载器没法完成此加载任务时,才本身去加载。
  • 缓存机制:缓存机制将会保证全部加载过的Class都会被缓存,当程序中须要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须从新启动JVM,程序所作的修改才会生效的缘由。

双亲委派机制的做用

一、防止重复加载同一个.class。经过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。 二、保证核心.class不能被篡改。经过委托方式,不会去篡改核心.class,即便篡改也不会去加载,即便加载也不会是同一个.class对象了。不一样的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

相关文章
相关标签/搜索