【Java】Java虚拟机-类的加载过程及双亲委派机制
原文链接
类的加载过程
加载
- 通过全类名获取类的二进制字节流
- 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区这个类的各种数据的方法入口
从何处获取字节流:
- 从本地磁盘获取
- 从网络获取
- 运行时计算生成(动态代理)
- 从其他文件生成(JSP文件)
- 从加密文件中获取
链接
验证
目的:确保Class文件的字节流中包含的信息符合《JAVA虚拟机的》全部约束要求,保证这些信息被当做代码运行后不会危害虚拟机自身的安全
验证的四个阶段
文件格式验证
- 目的是保证输入的字节流能够正确的解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求
元数据验证
- 目的是对类的元数据信息进行语义校验,保证不存在与《Java虚拟机》定义相悖的元数据信息
- 字节码验证
目的是通过数据流分析和控制分析,确定程序语义是合法的且符合逻辑
符号引用验证
- 目的是确保解析行为可以正常执行
准备
- 为类变量分配内存并设置变量初始值的阶段
基本类型的初始值
数据类型 | 零值 |
---|---|
int | 0 |
long | 0L |
short | (short)0 |
char | ''\u0000' |
byte | (byte)0 |
boolean | false |
float | 0.0f |
double | 0.0d |
reference | null |
解析
目的:Java虚拟机将常量池内的符号引用替换为直接引用
解析范围:
- 类和接口的解析
- 字段解析
- 方法解析
- 接口方法解析
初始化
初始化类变量和其他资源。初始化阶段就是执行类构造器<clinit>()
方法的过程。<clinit>()
是Java编译器的自动化产物。
<clinit>()
是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序是由在源文件中出现的顺序决定的- 在子类
<clinit>()
方法执行前,父类的<clinit>()
方法会执行完毕 - 如果一个类或接口中没有静态语句块或变量赋值操作,编译器不会为这个类生成
<clinit>()
方法
类加载器
引导类加载器
- 通过C或C++语言实现,嵌套在JVM内部
- 不继承
java.lang.ClassLoader
,没有父加载器 - 加载扩展类加载器和应用类加载器
自定义类加载器
- 使用Java语言编写
- 存在包含关系
启动类加载器(Bootstrap ClassLoader)
- 使用c/c++语言实现,嵌套在JVM内部
- 用来加载核心类库
- 不继承子
java.lang.ClassLoader
, 没有父加载器 - 加载扩展类和应用程序类加载器,并制定为他们的父类加载器
- Bootstrap启动类加载器只加载包名为java、java、sun等开头的类
扩展类加载器(ExtClassLoader)
- Java语言编写
- 派生于ClassLoader类
- 父类加载器为Bootstrap ClassLoader
系统类加载器(AppClassLoader)
- Java语言编写
- 派生于ClassLoader类
- 父类为ExtClassLoader
- 负责加载环境变量classpath或系统属性java.class.path指定路径下的类
- 是默认的类加载器
如何实现自己的类加载器?
- 实现
ClassLoader
类,并重写findClass()
方法
为什么要自定义类加载器?
- 隔离加载类
- 修改类加载的方式
- 扩展加载源
- 防止源码泄露
双亲委派机制
原理:
- 如果一个类加载器收到一个类加载请求,它不会自己先去加载,而是把这个请求委托给父类加载器去执行
- 如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的Bootstrap ClassLoader
- 如果父类加载器可以完成类加载任务,就成功返回,若父类加载器无法完成加载任务,子加载器才会尝试自己去加载
优势
- 避免类的重复加载
- 保护程序安全,防止核心API被随意篡改
其他
在JVM中表示两个class对象是否为同一个类存在的必要条件
- 类的全类名必须相同
- 类加载器必须是统一个
以上是 【Java】Java虚拟机-类的加载过程及双亲委派机制 的全部内容, 来源链接: utcz.com/a/93202.html