Java虚拟机--类加载机制

1.什么是类加载机制java

  虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终造成能够被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。程序员

2.类的加载时机安全

            

  图中的七个阶段表明这类的生命周期,其中加载验证准备初始化卸载的顺序是肯定的,按这种顺序循序渐进的“开始”,而解析则不必定:它在某些状况下能够在初始化阶段以后再开始,这是为了支持Java语言的运行时绑定。另外要注意:刚才的5个阶段一般都是相互交叉地混合式进行,一般会在一个阶段执行的过程当中调用、激活另一个阶段。数据结构

  加载阶段什么时候开始没有作强制约束,但对于初始化阶段,虚拟机规范则规定了有且只有5种状况必须对类进行“初始化”:  函数

  ■ 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时。触发场景:new关键字实例化对象、读取或设置一个类静态字段(被final修饰、已在编译器放入常量池的除外)、调用另外一个类的静态方法。this

  ■ 使用java.lang.reflect包的方法对类进行反射调用时。spa

  ■ 初始化一个类时,若是发现其父类还没初始化,就要先初始化父类。指针

  ■ JVM启动时,用户指定一个可执行的主类,先初始化这个主类。code

  ■ 当使用JDK 1.7动态语言支持时,若是一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,切方法句柄所对应的类没有初始化时。对象

3.类的加载过程

  加载

  加载阶段JVM完成件事:

  ■ 经过类的全限定名来获取定义此类的二进制数据流。(若是没有找到对应类文件,只有在类实际使用时才抛出错误)  

  ■ 将数据流分析并转化为方法区(JVM对运行数据区的划分结构,包括堆、栈、方法区等)的运行时数据结构。另外,这里处理了部分检验,如对类文件的魔数的验证、检查文件是否过长或太短、肯定是否有父类。

  ■ 在内存中生成一个表明这个类的java.lang.class对象,做为方法区这个类的各类数据的访问入口。

  链接

  链接指的是将Java类的二进制代码合并到JVM的运行状态之中的过程,链接以前必须被成功加载。链接分三个步骤:

  ■ 验证:用来确保Java类的二进制表示在结构是是彻底正确的、符合虚拟机规范的。如文件格式验证(是否以魔数0xCAFEBABE开头、主次版本号是都被虚拟机支持、常量类型是否都被支持)、元数据验证(就是语义分析,如是否有父类、这个类的父类是否继承了不被容许的类等)、字节码验证(经过数据流和控制流分析,肯定程序语义是合法的)、符号引用验证(将符号引用转化为直接引用,发生在解析阶段),若是验证出现错误的话,会抛出java.lang.verifyError错误异常。

  ■ 准备:正式为类变量(被static修饰)分配内存并设置类变量初始值(数据类型的零值)的阶段,这些变量所使用的内存都将在方法区(持久代)中进行分配。

  ■ 解析:将常量池内的符号引用替换为直接引用的过程,目的是确保这些被引用的类能被正确的找到。符号引用:以一组符号来描述引用的目标,符号能够是任意无歧义的字面量。直接引用:能够是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。

  初始化

  类的初始化是延迟的,直到第一次被主动使用(active use),JVM才会初始化类。初始化过程的主要操做是执行静态代码块和初始化静态域,在一个类被初始化以前,它的直接父类也要被初始化,可是一个接口的初始化不会引发其父接口的初始化。

4.类加载的推进者--类加载器

  Java中的全部类,必须被加载到JVM中才能运行,这个加载的工做是由JVM中的类加载器完成,类加载器的实质是把类文件从硬盘读取到内存中,JVM加载类时都是经过ClassLoaderloadClass()方法,此方法使用了双亲委派模式

  接下来咱们看loadClass方法中双亲委派的实现:

    

  图中有一个同步代码块synchronized (getClassLoadingLock(name)),咱们来看getClassLoadingLock(name)的做用是什么:

    

  咱们这里看到parallelLockMap变量,根据这个变量进行不一样的操做,若是变量是null,直接返回this;若是不为null,就新建一个对象,调用putIfAbsent方法给刚建好的对象赋值,那这个变量的来源是哪?

    

  咱们发现这个变量是ClassLoader类的成员变量,这个变量的初始化工做在ClassLoader的构造函数中:

    

  这里咱们能够看到构造函数根据一个属性ParallelLoadersRegistered状态的不一样来给parallelLockMap 赋值。 那ParallelLoaders又来自何方呢?

    

  咱们发现,在ClassLoader类中包含一个静态内部类private static class ParallelLoaders,在ClassLoader被加载的时候这个静态内部类就被初始化。这个类的意思就是:封装了并行的可装载的类型的集合

  综上源码,getClassLoadingLock(String className)的做用:为类的加载操做返回一个锁对象。为了向后兼容,若是当前的classloader对象注册了并行能力,方法返回一个与指定的名字className相关联的特定对象,不然,直接返回当前的classloader对象。咱们看到,classloader源码中还有一个resolveClass方法,它的做用是:连接指定的类,这个方法给classloader用来连接一个类,若是这个类已经被连接过了,那么这个方法只作一个简单的返回;不然,这个类将被按照Java™规范中的Execution描述进行连接。

5.类加载器总结

  java中的类大体分三种:系统类、扩展类、程序员自定义的类。

  类加载方式有两种:隐式加载,程序在运行过程当中碰到new关键字等方式生成对象时,隐式调用类加载器;显式加载,经过Class.forName()等方法,显式加载类。

  类加载的动态性体现:当运行一个由n多类组成的应用程序时,它会先把保证程序运行的基础类一次性加载入JVM中,其余类当用到时再进行加载,这样节省了内存开销。

  Java类加载器:加载器的实质也是类,功能是把类加载入JVM,但JVM的加载器有三个,一方面是是各自负责各自的区块,另外一方面实现委托模型。其层次结构:

        

  类加载器之间协调工做:那当咱们须要加载一个类时,是由哪个加载器来加载的呢?在这里Java采用委托模型机制,这个机制就是“类加载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,若是Parent找不到,再依照本身的搜索路径搜索类”。好比咱们本地有个Test类,调用Test.class.getClassLoader()时得到的加载器为AppClassLoaderAppClassLoader的父加载器为ExtClassLoaderExtClassLoader的父加载器为根加载器,但在IDE控制台打印时为null,是由于它由C++编写,在Java中看不到它。默认状况下,采用AppClassLoader加载程序类

  采用“委托机制”是从安全考虑的。试想,若是自定义了一个"java.lang.string"的恶意类,且其被加载入JVM中,会引发严重的后果。而采用“委托机制”,这个类永远只会由跟加载器加载。

相关文章
相关标签/搜索