前言

最近学习JVM调优相关的知识,了解到了有关类加载器有关的知识,总结一下.


叙述

java类加载机制

Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。


类加载过程

类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。

其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。
深入JVM-类加载机制

加载

概念:
    类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象
过程:

  1. 装载类的第一个阶段
  2. 取得类的二进制流
  3. 转为方法区数据结构
  4. 在Java堆中生成对应的java.lang.Class对象

验证

概念:
    目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
分类:

  • 文件格式的验证
    • 是否以0xCAFEBABE开头
    • 版本号是否合理
  • 元数据验证
    • 是否有父类
    • 继承了final类?
    • 非抽象类实现了所有的抽象方法
  • 字节码验证 (很复杂)
    • 运行检查
    • 栈数据类型和操作码数据参数吻合
    • 跳转指令指定到合理的位置
  • 符号引用验证
    • 常量池中描述类是否存在
    • 访问的方法或字段是否存在且有足够的权限

准备

概念:
    为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
详情:

  • 分配内存,并为类设置初始值 (方法区中)
    例如:
    public static int v=1;
    1.在准备阶段中,v会被设置为0
    2.在初始化的中才会被设置为1
    3.对于static final类型,在准备阶段就会被赋上正确的值
        public static final int v=1;

解析

    主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
深入JVM-类加载机制

初始化

概念:
    加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

  • 执行类构造器
    • static变量 赋值语句
    • static{}语句
  • 子类的调用前保证父类的被调用
  • 是线程安全的

这便是类加载的5个过程,而类加载器的任务是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例,在虚拟机提供了3种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器)

类加载器种类

    类加载器的任务就是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例。

  1. ClassLoader是一个抽象类
  2. ClassLoader的实例将读入Java字节码将类装载到JVM中
  3. ClassLoader可以定制,满足不同的字节码流获取方式
  4. ClassLoader负责类装载过程中的加载阶段

BootStrap ClassLoader (启动ClassLoader)

启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。

Extension ClassLoader (扩展ClassLoader)

扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。

//ExtClassLoader类中获取路径的代码
private static File[] getExtDirs() {
     //加载<JAVA_HOME>/lib/ext目录中的类库
     String s = System.getProperty("java.ext.dirs");
     File[] dirs;
     if (s != null) {
         StringTokenizer st =
             new StringTokenizer(s, File.pathSeparator);
         int count = st.countTokens();
         dirs = new File[count];
         for (int i = 0; i < count; i++) {
             dirs[i] = new File(st.nextToken());
         }
     } else {
         dirs = new File[0];
     }
     return dirs;
 }

App ClassLoader (应用ClassLoader/系统ClassLoader)

也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java
-classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。

Custom ClassLoader(自定义ClassLoader

在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式


类加载器间的关系
  • 启动类加载器,由C++实现,没有父类。
  • 拓展类加载器(ExtClassLoader),由Java语言实现,父类加载器为null
  • 系统类加载器(AppClassLoader),由Java语言实现,父类加载器为ExtClassLoader
  • 自定义类加载器,父类加载器肯定为AppClassLoader。

小结

在学习的过程中,一定要总结.
参考博客链接

感谢您的阅读~~

相关文章: