Java类加载机制详解
时间:2022-07-24
本文章向大家介绍Java类加载机制详解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1.概述
Java类加载器负责加载所有的类,系统会为所有被载入内存的类生成一个java.lang.Class实例。对于同一个类,一旦被加载如内存中,就不会被再次加载。JVM使用一个类的权限的类名和该类的加载器唯一地标识一个类。因此即使两个类的包名、类名完全相同,但是使用不同的类加载器加载,这两个类也会被认为是不同的。 当程序首次使用某个类时,如果该类还未被加载,则系统会通过以下三个步骤加载该类:
- 加载:查找和载入Class字节码文件
- 连接:执行校验、准备和解析三个步骤 2.1校验:检查载入的Class文件的正确性,并和其他类协调一致 2.2准备:为类的静态变量分配内存,并设置默认初始值 2.3解析:将类的符号引用转换成直接引用
- 初始化:对类的静态变量、静态代码块执行初始化工作
有时,也将上述三个步骤统称为类的加载。
2.类加载器的层次结构
当JVM启动时,会形成由三个类加载器组成的初始化类加载器层次结构:
- Bootstrap CLassLoader:根类加载器,也称为引导类加载器,负责加载Java的核心类,如JRE目标下的tr.jar、charsets.jar等。根类加载器并不是ClassLoader的子类,它采用C++编写,在Java程序中无法获得。
- Extension ClassLoader:扩展类加载器,它负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext)下的jar包。
- Application ClassLoader:应用类加载器,也称为System ClassLoader(系统类加载器),主要负责加载classpath路径下的jar包。
三个类加载器之间存在父子层级关系,即Bootstrap ClassLoader是Extension ClassLoader的父类加载器,Extension ClassLoader是Application ClassLoader的父类加载器。这里类加载器的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。 默认情况下,Java程序使用Application ClassLoader加载一个类。示例程序如下:
public class CLassLoaderTest {
public static void main(String[] args) {
//获取当前线程下的ClassLoader
ClassLoader current = Thread.currentThread().getContextClassLoader();
System.out.println("current:" + current);
//获取父类加载器
System.out.println("parent:" + current.getParent());
System.out.println("grandparent:" + current.getParent().getParent());
}
}
输出结果:
current:sun.misc.Launcher$AppClassLoader@56e88e24
parent:sun.misc.Launcher$ExtClassLoader@3dcc0a0f
grandparent:null
由此可见,Java程序默认采用Application ClassLoader,Extension ClassLoader为Application ClassLoader的父类加载器。由于Bootstrap CLassLoader由C++编写,在程序中无法获取,所以返回null。
3.类加载机制
JVM采用如下三种类加载机制:
- 全盘委托:当一个类加载器负责加载某个类时,该类所依赖和引用的其他类也将由该类加载器负责加载,除非显式指定另一个类加载器。
- 父类委托:加载一个类时,首先会让其父类加载器进行加载,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载。这一机制可以保证类加载的安全性。
- 缓存机制:JVM会缓存所有已经加载过的类,当程序使用某个类时,会首先在缓存中搜寻该类,只有缓存中不存在时才会加载。这也是为什么修改了一个类后,只有重启JVM才会生效。
为了更好地说明JVM的类加载机制,对ClassLoader的loadClass()方法的源码分析如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查该类是否已经被加载,如果已经加载过,则直接返回该Class(缓存机制)
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//如果该类未被加载,首先尝试获取父加载器,如果父加载器不为null,则使用父加载器进行加载(父类委托机制)
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//如果父加载器为空,说明当前加载器为ExtensionClassLoader 或 BootstrapCLassLoader,则使用根类加载器进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//如果使用根类加载器加载失败,则抛出 ClassNotFoundException
}
if (c == null) {
//如果父加载器未能成功加载,则使用当前的类加载器进行加载
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释