介绍
介绍JVM类加载机制。类加载机制生命周期:
一、加载class文件到内存步骤
第一步:加载
加载需要做三件事情:
这个文件在哪个位置?是jar包还是class文件(格式)?
1
2java TestClass
java -jar获取二进制字节流。
静态存储结构转化为方法区运行时数据结构
- 在java堆中生成一个Class对象,相对于一个句柄,去访问方法区
第二步:验证
- 验证Class文件的标识:魔数Magic Number
- 验证Class文件版本号
- 验证常量池
- 常量类型(14个)
- 常量类型数据结构是否正确
- UTF8是否符合标准
- Class文件的每个部分(字段表、方法表等)是否正确
- 元数据验证
- final的验证
- 父类验证
- 继承验证
- 字节码验证
- 指令验证
- 符号引用验证
- 通过符号引用是否能够找到字段、方法、类
第三步:准备
为类变量分配内存病区设置类变量的初始化阶段。只对static类变量进行内存分配。
- 类变量:一般称为静态变量
- 实例变量:当对象被实例化的时候,实例变量就跟着确定,随着对象销毁而销毁
1 | static int n = 2; |
第四步:解析
队符号引用进行解析。把符号引用指向直接引用。
- 直接引用:指向目标的指针,或者一个偏移量
- 符号引用:以字面量形式定义在常量池中
主要涉及:
- 类
- 接口
- 字段
- 方法(接口方法、类方法)
1 | CONSTANT_Class_info |
匹配规则:简单名字+描述符,同时满足。
字段解析
1
2
3class A extends B implements C,D {
private String str; //字段的解析
}解析字段的顺序:
A--->C,D--->B--->Object
。- 在本类中去找有没有匹配的字段,找到则返回
- 如果类有接口,往上层接口找匹配的字段,没有则下一步
- 搜索父类查找有没有匹配的字段
- 最后是到Object基类查找
- 如果没找到:
java.lang.NoSuchFieldError
。 - 如果找到了,但是没有权限(private):
java.lang.IllegalAccessError
类方法解析
1
2
3class A extends B implements C,D {
private void inc(); //方法的解析
}方法解析顺序:
A—>B—>C,D—>Object`。
- 在本类中去找有没有匹配的方法
- 父类中去找匹配的方法
- 接口列表中去找匹配的方法
- 如果没找到:
java.lang.NoSuchMethodError
- 如果找到了,但是没有权限(private):
java.lang.IllegalAccessError
接口方法解析
- 在本类中查找有没有匹配的方法,找到则返回
- 父类接口递归查找
- 如果没找到:
java.lang.NoSuchMethodError
- 接口都是public,不需要进行权限检查
第五步:初始化
1 | //<init>:类的实例构造器,类的初始化 |
第六步:使用
第七步:卸载
二、类加载器
机制:双亲委派,目的:安全
- 父类加载的不给子类加载
- 一个类只能加载一次
三、双亲委派模式
为什么需要双亲委任?
黑客自定义一个java.util.List
类,该List类具有系统的List类一样的功能,只是在某个函数稍做修改。这个函数经常使用,假如在这个函数中植入一些“病毒代码”,并且通过自定义类加载器加入JVM中。
四、自定义类加载器
自定义类加载器打破双亲委任模式。
1 | public class CustomizeClassLoader { |