1.类加载机制
JVM把class文件加载到内存,并对数据校验,解析和初始化,最终形成JVM可以直接使用的java类型的过程
1.1加载
将class文件字节码加载到内存,并将这些静态数据转化成方法区中运行时的二进制的数据结构,在堆中生成一个代表类的java.Lang.Class,作为方法区类数据的访问入口
1.2链接
将java的二进制代码合并到jvm的运行状态之中的过程
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
- 准备:正式为变量(static变呈)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
- 解析:虚拟机常量池内的符号弓用替换为直接引用的过程
1.3初始化
- 初始化阶段是执行类构造器clinit()方法的过程。类构适器clinit()是由编译器自动收集
类中的所有类变量的赋值动作和静态语句块(static块)中的语法合并产生的· - 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先对其父类初始化
- 虚拟机会保证一个类的cilnit()方法在多线程环境中被正确加锁和同步
发生类的初始化:类的主动引用
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 运行main方法所在的类
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先对其父类初始化
不会发生类的初始化:类的被动引用
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。通过子类引用父的静态变量不导致子初始化
- 沮叟类引用,不会岌此类的初始化
- 引用此类常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
2.深入类加载器
2.1.类加载器的层次结构
-
引导类加载器:bootstrapClassLoader 虚拟机自带的加载器
加载java核心库
不继承java.lang.classLoader
加载扩展类加载器和应用程序类加载器(系统类加载器)
原生代码实现 -
扩展类加载器:extClassLoader
加载java扩展库
加载应用程序类加载器 (系统类加载器)
继承java.lang.classLoader -
应用程序类加载器(系统类加载器):systemClassLoader
根据java应用的类路径加载java应用的类
继承java.lang.classLoader -
自定义类加载器:
public class ClassLoaderTest {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@677327b6
//继续获取上层:获取不到引导类加载器,引导类加载器使用C语言和C++编写
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
//用户自定义类:默认使用系统类加载器加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2\
//String类使用引导类加载器加载,系统的核心类库都是用引导类加载器加载
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);
}
}
2.2.双亲委托机制
Java虛拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。
工作原理:
1、如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
2、如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达项层的启动类加载器;
3、如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务(有的加载器只能加载特定的类,如Java核心库类),子加载器才会尝试自己去加载,这就是双亲委派模式。
优点:
- 避免类被重复加载
- 保护程序安全,防止API被篡改(沙箱安全机制)
java.lang.SecurityException: Prohibited package name: java.lang
在JVM中表示两个class对象是否为同一个类存在两个必要条件:
1、类的完整类名必须一致,包括包名。
2、加载这个类的ClassLoader (指ClassLoader实例对象)必须相同。.