dev

Java Basics - Java Class Loading

/note-bak/dev/java/classloading/

Java Class Loading

Java类加载机制

Compilation and Loading Process

  • 编译:通过javac将java源文件编译为class字节码文件。
  • 加载:jvm将class file读到内存中,对其进行校验、转换解析、初始化,最终在堆heap分配内存,形成可以被jvm直接使用的类型。

Class Lifecycle

五步:

  • 加载 Loading

  • 加载 class 文件

  • 验证 Verification

  • 检查 class 文件格式

  • 准备 Preparation

  • 分配内存空间

  • 解析 Resolution

  • 将(常量池中的)符号引用变为直接引用

  • 初始化 Initialization

  • 对 static 变量/常量赋予初始化值

Loading

简单来说,就是把Java文件“读”进JVM中。

  • 读取:类加载器通过类的全路径限定名读取类的二进制字节流,
  • 转化:将二进制字节流代表的类结构转化到运行时数据区的方法区中,
  • 生成实例:在jvm堆中生成代表这个类的java.lang.Class实例(不是这个类的实例)。

Class Loaders

  • 启动类加载器 Bootstrap classloader:用c++实现为jvm的一部分(仅指sun的hotspot),负责JAVA_HOME/lib下面的类库中的类的加载
  • 扩展类加载器 Extension classloader:由sun.misc.Launcher$ExtClassLoader类实现,负责JAVA_HOME/lib/extjava.ext.dir中类库的加载
  • 应用类加载器 App classloader:由sun.misc.Louncher$AppClassLoader实现,负责加载用户类路径中类库中的类(如果没有自定义的加载器,这个就是默认的加载器)
  • 用户自定义加载器

Parent First Model

双亲委托模型:优先由父加载器尝试加载,不行的话再一级一级往下

  • 加载一个类时,类加载器首先检查自己是否已经加载了该类,如果已加载,则返回;否则请父加载器代理
  • 父加载器重复1的操作一直到最顶层的Bootstrap Class Loader
  • 如果Bootstrap Class Loader也没有加载该类,将尝试进行加载,加载成功则返回;如果失败,抛出ClassNotFoundException
  • 子类加载器捕捉异常后尝试加载,如果成功则返回,如果失败则抛出ClassNotFoundException,一路向下,直到发起加载的子类加载器。

为什么采用Parent First代理模式?为了保证Java核心库都是由Java自己的加载器Bootstrap classloader加载完成的。 因为判断两个类是否相同,有两个因素:1是类文件的内容,2是由什么加载器加载。

Parent Last Model

  • 子加载器首先尝试加载,如果加载失败才会请父加载器
  • 比如 IBM Web Sphere Portal Server 实现的一些类加载器

Verification

加载完了Class文件,需要验证,主要有两个方面:一是内容规范检验,二是安全性检验(是否会危害虚拟机自身的安全)。

验证字节码文件是否合格:

  • 文件格式:参看class文件格式详解;
  • 元数据验证:是否符合java语言规范;
  • 字节码验证:数据流和控制流的分析,这一步最复杂;
  • 符号引用验证:符号引用转化为直接引用时(解析阶段),检测对类自身以外的信息进行存在性、可访问性验证。

Preparation

正式为类变量(被static修饰的变量)分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

public static int value=123;

经过准备阶段,value = 0,经过初始化阶段后,value = 123。

public final static int value=123;

经过准备阶段,value = 123

Resolution

解析阶段是JVM将常量池(Constant Pool)内的符号引用替换为直接引用的过程。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

Initialization

对Class Variables进行初始化赋值。