java 类加载机制

  • 时间:2015-08-26
  • 方式:内容来自《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》

类的生命周期


类从被加载到虚拟机内存开始直到卸载为止;类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载这几个过程;

我们通常说的class load的过程包括:加载、验证、准备、解析、初始化 这几个阶段;

加载

加载阶段主要完成3件事情:

  • 通过类的全限定名称获取此类的二进制流
  • 通过字节流的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生存一个java.lang.Class对象;作为方法区的各种数据的访问入口

验证

验证加载的类的字节流是否合法;主要是为了避免虚拟机收到攻击;大致分为4个阶段;

  • 文件格式验证:魔数、版本号、常量的类型、索引及编码规范等
  • 元数据验证:是否有父类、是否继承了final类、是否是抽象类、是否实现了所以的父类方法、字段方法是否冲突
  • 字节码验证:字节码指令是否符合预期;(如入栈是int出栈也应该是int)、跳转范围是否合理、类型转化是否合理等
  • 符号引用验证:引用是否有对应类,是否存在符合方法描述的方法、字段,要求访问的字段、方法是否可访问等

准备

进行内存分配,对static变量赋值;注意以下两种写法;

public static int aa = 123;         //准备后为0
public static fianl int bb = 123;   //准备后为123

第一种情况实际上是吧赋值放到了 方法内;所以此处并不赋值;常见的初始值情况如下:

  • int:0
  • boolean:false
  • long:0L
  • float:0.0f
  • short:(short)0
  • double:0.0d
  • char:'\u0000'
  • reference:null
  • byte:(byte)0

解析

将符号引用转化为直接引用;

符号引用不要求虚拟机去加载;只要能找到定义即可; 直接引用可以理解为指向目标的指针;有了直接引用;对象必已存在;

几个和解析有关的异常:

  • java.lang.IllegalAccessError:被引用对象(类、接口、方法)解析成功;但是引用方没有权限访问;
  • java.lang.NoSuchFieldError:解析字段符号引用时;无法在定义类中、定义类的实现接口、类中找到定义时抛出;
  • java.lang.NoSuchMethodError:解析类引用时;无法在定义类中、定义类的实现接口、类中找到定义时抛出;

初始化

执行方法;方法是编译器收集类中的静态变量赋值或者是statis区域后自动生成的;

类加载器


被不同类加载器加载的类哪怕拥有相同的全限定名称;也会被认为是两个不同的类;在java中;类加载器使用双亲委派模型;

被成为双亲委派是因为在java虚拟机的角度上;只有两种加载器;

一是Bootsrap ClassLoader,使用C++实现;是虚拟机一部分;另一种是其他的类加载器;

但是我们更习惯与细分,如下(图片来自网络):

sqwp.png

  • Bootsrap ClassLoader:加载/lib下和Xbootclasspath指定的类;在java中得到此加载器的classload会返回null
  • Extension Classloader:扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包
  • System Classloader:系统(也称为应用)类加载器;负责加载用户的类;一般来说用户默认的加载器就是这个;

如图所示,双亲委派的模型的工作流程为;如果一个类加载器收到了加载请求;首先会尝试将加载任务委托给自己的父类完成;只有当父类加载器无法完成加载请求时才会自己加载;这样可以避免用户自己定义的类污染jvm环境;