类的生命周期
验证、准备、解析为连接
解析与初始化交换顺序为动态绑定
类加载过程
包含了加载、验证、准备、解析、初始化阶段
1、加载
2、验证
确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机本身
- 文件格式验证:验证字节流是否符合 Class 文件格式的规范,并且能被当前版本的虚拟机处理。
- 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合 Java 语言规范的要求。
- 字节码验证:通过数据流和控制流分析,确保程序语义是合法、符合逻辑的。
- 符号引用验证:发生在虚拟机将符号引用转换为直接引用的时候,对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验。
3、准备
准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。
- 1、为类变量分配内存(方法区内存)
- 2、为类变量设置初始值(数据类型的初始值)
ps:类变量是被 static 修饰的变量
如果字段声明为常量(final static)则在准备阶段赋值为java代码中定义的值
4、解析
将常量池的符号引用替换为直接引用的过程。
- 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。
5、初始化
初始化阶段才真正开始执行类中的定义的 Java 程序代码。初始化阶段即虚拟机执行类构造器
- 初始化变量是从上到下依次初始化
初始化实例变量(实例变量会在对象实例化时随着对象一块分配在Java堆中),静态变量
- 虚拟机会保证
如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成
ps:初始化时机
- (1) 创建类的实例,也就是new的方式
- (2) 访问某个类或接口的静态变量,或者对该静态变量赋值
- (3) 调用类的静态方法
- (4) 反射(如Class.forName(“com.shengsiyuan.Test”))
- (5) 初始化某个类的子类,则其父类也会被初始化
- (6) Java虚拟机启动时被标明为启动类的类(包含main()方法的那个类),直接使用java.exe命令来运行某个主类
ps:以下几种情况不会执行类初始化:
- 通过子类引用父类的静态字段,只会触发父类的初始化,子类不会初始化
- 定义对象数组,不会触发该类的初始化
- 常量在编译期间会存入常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量的类
- 通过类名获取Class对象,不会触发类的初始化
- 通过Class.forName加载指定类时,如果指定参数initialize为false,也不会进行初始化,这个参数是告诉虚拟机,是否有对类进行初始化
- 通过ClassLoader默认的loadClass方法,也不会触发初始化
6、结束生命周期
- 执行了System.exit()方法
- 程序正常执行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统出现错误而导致Java虚拟机进程终止
---
类加载器
在 Java 虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。
1、类与类加载器
两个类相等:类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。
2、类加载器分类
3、双亲委派模型
一个类加载器首先将类加载请求传送到父类加载器,只有当父类加载器无法完成类加载请求时才尝试加载。
好处:保证使用不同类加载器最终得到的是同一个对象
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
- 首先通过Class c = findLoadedClass(name);判断一个类是否已经被加载过。
- 如果没有被加载过执行if (c == null)中的程序,遵循双亲委派的模型,首先会通过递归从父加载器开始找,直到父类加载器是Bootstrap ClassLoader为止。
- 最后根据resolve的值,判断这个class是否需要解析。
ClassLoad 类加载
原文:https://www.cnblogs.com/mingyi123/p/9383901.html