
JVM的类加载机制是一个很经典的知识点,阅读本文,你将了解类文件的组成、JVM中的类加载过程、类加载和卸载的时机、类加载器的概念和作用以及双亲委派模型等知识点。
1、类文件机器只认机器码,所谓的机器码, 就是机器识别的一堆有特殊意义的二进制指令。JVM作为虚拟的机器,也抽象出了一套自己的“指令集”,这些信息都存在Class文件里,Class文件是以二进制形式存储,并严格的规范了各个字段的语义, 兼容性和检查方法,Class 文件具体由:魔数、版本信息、常量池、访问标志、类索引、父类索引、接口索引集合、字段表集合、方法表集合、属性表集合构成,JVM8规范给出的结构如下:
ClassFile {
u4 magic; //魔数 表示这个Class文件的类型,Java的魔数是0xCAFEBABE,翻译是咖啡宝贝
u2 minor_version;//版本信息 Class的小版本号
u2 major_version;//版本信息 Class的大版本号
u2 constant_pool_count;//常量池的数量
cp_info constant_pool[constant_pool_count-1];//常量池 主要存放两大常量:字面量和符号引用
u2 access_flags;//访问标志 用于识别一些类或者接口层次的访问信息
u2 this_class;//类索引 用于确定这个类的全限定名
u2 super_class;//父类索引 用于确定这个类的父类的全限定名
u2 interfaces_count;//接口索引数量
u2 interfaces[interfaces_count];//接口索引集合 描述这个类实现了哪些接口
u2 fields_count;//字段表集合数量
field_info fields[fields_count];//字段表集合 存储本类涉及到的成员变量,包括实例变量和类变量,但不包括方法中的局部变量
u2 methods_count;//方法表集合数量
method_info methods[methods_count];//方法表集合信息
u2 attributes_count;//属性表数量
attribute_info attributes[attributes_count];//属性表信息 存储信息量最大的一块结构,里面包含了方法编译后生成的字节指令(Code), 注解信息等等
}
2、类的生命周期
Java类从被虚拟机加载开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段;其中验证、准备和解析又统称为连接阶段。
JVM将指定的class文件读取到内存里,并运行该class文件里的Java程序的过程,就称之为类的加载;反之,将某个class文件的运行时数据从JVM中移除的过程,就称之为类的卸载。
Java 虚拟机规范没有强制约束类加载过程的第一阶段(即:加载)什么时候开始,类加载的最佳时机是解析Java字节码类文件中常量池符号的时候,Class.forName()、ClassLoader.loadClass()、反射API和JNI_FindClass都可以触发类加载,Hot JVM自身启动的时候也会触发类加载。
卸载类即该类的 Class 对象被 GC,类的卸载跟采用的垃圾收集算法有关,在这里,我们只需要记住JVM中一个类的卸载要满足下面这3个条件就行:
从上面的条件中,我们知道,在 JVM 生命周期内,由 JVM 自带的类加载器加载的类是不会被卸载的,而我们自定义加载器加载的类是可以被卸载掉的。
3、四种类加载器从JVM的角度看,类加载器主要有两类:Bootstrap ClassLoader和其他类加载器,Bootstrap ClassLoader是C++语言实现,是虚拟机自身的一部分;其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。
最上面是JVM 中内置了三个重要的 ClassLoader,我们也可以自定义类加载器,通常在以下几种情况下可能需要自定义类加载器:
在JVM中,一个类的唯一性是需要这个类本身和类加载一起才能确定的,每个类加载器都有一个独立的命名空间。不同的类加载器,即使是同一个类字节码文件,在JVM里的类对象也不是同一个。
因此,类加载器在JVM中的作用除了将类的字节码文件从JVM外部加载到内存中,还确定了一个类的唯一性。
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。
3.3、破坏双亲委派模型阅读完本文,对于Java的动态性,如热部署,动态编译JSP, 运行时增强类功能等, 是不是又有了新的认识:)