Java 类加载和类加载器
类加载
Java 的类加载阶段分为:加载、链接、初始化,而链接的过程中包括:验证、准备、解析
加载
将类的字节码载入方法区中,内部采用 C++ 的 instanceKclass 描述 Java 类
instanceKclass 的重要字段:
_java_mirror
:Java 类镜像,存放类对象的地址,例如对于 String,存放的就是String.class_super
:即父类_methods
:即方法_constants
:即常量池_class_loader
:类加载器_vtable
:虚方法表_itable
:接口方法表
链接
链接阶段包括验证、准备、初始化三个部分
验证
验证类是否符合 JVM 规范,进行安全性检查,例如:检查 Java 文件的魔数
准备
为 static 变量分配地址空间,设置默认值
- static 变量分配空间和赋值是两个步骤,分配空间是在准备阶段完成,而赋值是在初始化阶段完成;
- 若 static 变量是 final 类型的,且变量类型为基本数据类型或者字符串对象,则在准备阶段进行赋值;
- 若 static 变量是 final 类型的,且变量类型为引用对象,则仍在初始化阶段进行赋值;
解析
将常量池中的符号引用解析为直接引用
初始化
初始化调用 <cinit>()v
,即执行类的构造方法,代码块等,虚拟机会保证这个类的线程安全
发生的时机
概括的说,类的初始化是懒惰的
- main 方法所在的类,总会被首先初始化;
- 首次访问类的静态变量或者静态方法,因为这些变量不是 final 的,会在类的初始化阶段进行赋值;
- 子类初始化,会引起父类的初始化;
- 子类方法父类的静态变量,会导致父类的初始化;
- Class.forName;
- new 会导致初始化;
不会触发类的初始化
- 访问类的
static final
静态变量(基本类型和字符串),不会被初始化,因为这些变量的赋值是在准备阶段; - 类对象
.class
不会触发类的初始化; - 创建该类的数组不会触发初始化;
- 类加载器的
loadClass
方法; Class.forName
的参数 2 为false
时
类加载器
以 JDK8 为例
名称 | 加载哪里的类 | 说明 |
---|---|---|
Bootstrap ClassLoader | JAVA_HOME/jre/lib | 无法直接访问 |
Extension ClassLoader | JAVA_HOME/jre/lib/ext | 上级为 Bootstrap ClassLoader,访问为 null |
Application ClassLoader | classpath | 上级为 Extension ClassLoader |
自定义类加载器 | 自定义 | 上级为 Application ClassLoader |
类加载器的作用:加载类的二进制字节码
双亲委派模式
双亲委派模式是一种Java类加载机制,它定义了一种层次化的父子关系,由父类加载器向下委派请求,直到找到合适的类加载器为止。
- 首先会检查缓存,查找该加载器是否已经加载过这个类了,如果没有就去父类的加载器中去寻找;
- 如果缓存中没有找到,就会自顶而下用的类加载器去创建该类;
最终返回该类即可;
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 检查缓存,是否已经加载过这个类
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 向上级的累加器中找
c = parent.loadClass(name, false);
} else {
// 向 Bootstrap 类加载器中找
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 如果缓存没有找到,就去创建
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 调用 findClass 去创建类
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
作者:EzreaLwj
链接:https://juejin.cn/post/7226665757882236986
还没有评论,来说两句吧...