Class 类加载机制
类的生命周期
包括以下 7 个阶段:
- 加载(Loading)
- 验证(Verification)
- 准备(Preparation)
- 解析(Resolution)
- 初始化(Initialization)
- 使用(Using)
- 卸载(Unloading)
类加载过程
加载
- 通过全限定类名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。
类加载器【ClassLoad】
- Bootstrap : 加载 lib、rt.jar 等核心类,由 C++ 编写【Java 识别不了,返回 null】
- Extension : 加载扩展 jar 包
- App : 自己定义的 ClassPath 指定内容
// sun.misc.Launcher$AppClassLoader@73d16e93 public class P { public static void main(String[] args) { System.out.println(P.class.getClassLoader()); } }
- Custom ClassLoader : 自定义的 ClassLoader
类加载器的流程
- 我们 .Class 文件去 Custom 缓存中寻找是否加载过该类,
Q:加载器之间不是继承关系,那么是怎么来下实现的?
A:通过引用的 Parent 来进行实现
双亲委派的原因
- 出于安全性考虑【防止程序员自己构建 Java.lang.Class 对象】
- 资源浪费问题【防止类重新进行加载】
双亲委派机制的打破
- 重写 LoadClass,改变其 Parent 的引用
- 何时打破过?
- JDK 1.2 之前,自定义 ClassLoader 都必须要重写 LoadClass()
- 热启动、热部署: 直接将 ClassLoad 全部干掉,重新进行加载 ClassLoad
验证
验证文件是否符合 JVM 规定,验证开头是否为 CAFEBABE
准备
给静态成员变量赋予默认值
解析
- 将类、方法、符号引用解析成直接引用
- 常量池中的各种符号引用解析成指针等内存地址的直接引用
初始化
- 调用类初始化代码,给静态变量赋予初始值
- 先进行赋予默认值(null / 0),再赋予初始值
// 输出的 T.count 的值 public static ClassLoading{ public static void main(String[] args){ System.out.println(T.count); } } class T{ public static T t = new T(); public static int count = 2; private T(){ count++; } }
总结
- load - 默认值 - 初始化
- new - 申请内存 - 默认值 - 初始化