一、类加载机制
1.类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Class文件加载到内存,这就是类加载机制。
2.类型的加载、连接和初始化过程发生在运行期。
3.Java动态扩展:依赖运行期动态加载与动态连接。
4.本地程序运行时Class文件可以来源于网络或者其他地方,称为程序的一部分,如OSGi技术。.
二、类加载时机
1.类的生命周期
- 加载、验证、准备、初始化和卸载:这5个阶段按照如此顺序开始
- 解析可能发生在使用阶段,来支持运行时绑定

2.初始化发生的5个条件(加载、验证、准备当然要提前完成)
- 遇到 new、 getstatic、 putstatic 或 invokestatic 这 4 条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
- 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类。
- 当使用 JDK1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果 REF_getStatic、 REF_putStatic、 REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
3.上述5个条件为对类的主动引用,其他引用类的方式不会触发初始化,称为被动引用
- 通过子类引用父类的静态字段
- 通过数组定义引用类,如Shape[] s = new Shape[10];
- 引用类的static final域,由于在编译阶段会存入调用类(使用者)的常量池,本质并没有直接引用到定义常量的类
三、类加载过程
1.加载
1)加载阶段需要完成的三件事
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(包括类型信息吧...)。
- 在内存中生成一个代表这个类的 java. lang. Class 对象,作为方法区这个类的各种数据的访问入口。
2)数组:不通过类加载器加载,由Java虚拟机直接创建的
2.验证
1)目的:自我保护
2)验证阶段工作量占类加载相当大一部分
3)具体检验动作
- 文件格式验证:魔数、版本号、常量池tag、编码等,本种验证基于二进制字节流,通过后,字节流进入内存的方法区储存,后面验证基于内存
- 元数据验证:语义分析,保证描述信息符合语言规范,如是否有父类、类的父类是否继承了不允许继承的类、如果为抽象类是否实现了所有抽象方法等
- 字节码验证:确定程序语义合法,如确定数据和指令可以配合工作、保证跳转指令不会跳转到方法体外的字节码指令上、保证类型转换有效(不出现将一个类型转换为另外一个无关类型)等,由于数据流验证的高复杂性,可通过StackMapTable优化
- 符号引用验证:符号引用转换为直接引用时(解析阶段),如符号引用通过字符串全限定名能否找到对应的类、是否存在所描述的方法和类、符号引用中的类、方法、字段的访问性是否可被当前类访问,其实并非必要!
3.准备
- 目的:正式为类变量分配内存并设置类变量的初始值,对于非final变量为0,对于final变量,需要执行其初始化流程
4.解析
- 将常量池的符号引用替换为直接引用
- 符号引用:与内存无关,直接引用:与内存有关
- 除了invokedynamic指令,JVM可能对第一次解析的结果进行缓存
- 类或接口解析、字段解析、类方法解析、接口方法解析
5.初始化
- 初始化阶段是执行<clinit>()的过程
- <clinit>():编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并生成
- 虚拟机保证子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕
- 保证执行<clinit>()的线程安全性
四、类加载器
1.类加载器
- 类加载过程:JVM之外实现根据一个类的全限定名来获取描述的二进制字节流
- 类层次划分、OSGi、热部署、代码加密均要依赖这个机制
- 每一个类加载器,有其独立的类命名空间,同一个类由不同类加载器加载,则加载的类不相等,包括Class对象的equals()、iAssignableFrom()、isInstance()、instanceof
2.双亲委派
1)三种类加载器:父子关系通过组合实现非继承
- 启动类加载器(Bootstrap ClassLoader):这个类将器负责将存放在
JAVA_HOME\lib
目录中的,或者被-Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar, 名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用 null 代替即可。
- 扩展类加载器(Extension ClassLoader):它负责加载
JAVA_ HOME\lib\ext
目录中的,或者被 java.ext.dirs 系统变量所指定的路径中的所有类库。
- 应用程序类加载器(Application ClassLoader):由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径( ClassPath) 上所指定的类库。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
2)工作过程

3.破坏双亲委派(OSGi)
参考:
https://gavinzhang1.gitbooks.io/java-jvm-us/content/xu_ni_ji_lei_jia_zai_ji_zhi.html
深入理解Java虚拟机5-chap7-斗者2星
原文:https://www.cnblogs.com/forTheDream1991/p/10479742.html