Class Loader Subsystem
类加载器子系统的作用
- 从文件系统或者网络中加载
class文件; -
class文件在开头有特定的标识; -
ClassLoader只负责class文件加载,是否可以运行由ExecutionEngine决定; - 加载的类信息被存放在方法区的内存空间,方法区还存在运行时常量池信息;
类的加载过程
- 加载-链接(验证、准备、解析)-初始化
加载
- 通过一个类的全限定名获取此类的二进制字节流;
- 从本地系统加载;
- 通过网络获取;
- 从压缩包中读取;
- 动态代理;
- 从加密文件中获取;
- 将这个字节流所代表的静态存储结构,转化为方法区的运行时数据结构;
- 内存中生成一个代表此类的
Class对象,作为方法区这个类的各种数据访问入口;
链接
- 验证-准备-解析
验证
- 验证class文件字节流中包含的信息符合虚拟机要求;
- 四种验证:
- 文件格式
- 元数据
- 字节码
- 符号引用
准备
- 为类变量分配内存并且设置类变量的初始值;
- 不包含final修饰的static变量值,在编译时候会分配值;
- 不会为实例变量分配初始化;
解析
- 将常量池中的符号引用转为直接引用;
初始化
- 执行类构造器方法
<clinit>; -
<clinit>是javac编译器自动收集类中的所有类变量赋值动作、静态代码块中的语句合并而来; - 构造器方法中的指令按照语句在源文件中出现的顺序执行;
-
<clinit>不同于类的构造器; - 若该类有父类,会保证父类的
<clinit>先执行; - 虚拟机保证同一个类的
<clinit>方法在多线程下被同步加锁;
类加载器分类
- JVM支持两类:引导类加载器、自定义加载器;
- Java将所有派生于抽象类
ClassLoader的类加载器都划分为自定义类加载器;
虚拟机自带的加载器
-
BootstrapClassLoader:启动类加载器,java核心类库使用引导类加载器;jre/lib/rt.jar、resource.jar;- 不继承自
ClassLoader; - 负责加载扩展类加载器和应用程序类加载器;
- 只加载包名为
java、javax、sun等头的类;
- 不继承自
-
ExtensionClassLoader:扩展类加载器- Java编写,继承与
ClassLoader jre/lib/ext/*- 如果用户创建的jar放在上面的目录,也会自动由扩展类加载器加载;
- Java编写,继承与
-
AppClassLoader:应用程序类加载器- 父类加载器为扩展类加载器;
- 负责加载环境变量
classpath下的类库; - 程序中默认类加载器;
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取上层:扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@61bbe9ba
//获取上层:无法获取引导类加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null
- 获取bootstrap能够加载的类的路径
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL url :urLs){
System.out.println(url);
}
/*
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/classes
*/
- 扩展类加载器加载的路径
String property = System.getProperty("java.ext.dirs");
for (String path:property.split(":")){
System.out.println(path);
}
/*
/Users/***/Library/Java/Extensions
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext
/Library/Java/Extensions
/Network/Library/Java/Extensions
/System/Library/Java/Extensions
/usr/lib/java
*/
- 获取类加载器的方式
//获取classloader的方式
//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader = StackStruTest2.class.getClassLoader();
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
双亲委派机制
- JVM对于class文件采用按需加载机制;
- 加载时采用双亲委派模式;
工作原理
- 如果一个类加载器收到加载类的请求,不会自己加载,而是把请求委托给父类加载;
- 如果父类加载器还存在父类加载器,则进一步向上委托,直到到达最顶层启动类加载器;
- 如果父类可以完成加载,成功返回,如果父类无法完成任务,则子类去加载;

15865028680504.jpg
优势
- 可以避免类重复加载;
- 可以避免核心API被篡改;(沙箱安全机制)
其他
- 在JVM中保证两个class对象是否为同一个类的两个必要条件:
- 类的完整类名需要一致;
- 加载这个类的
ClassLoader的实例对象必须相同; - 在jvm中,就算来源于同一个class文件,但是被不同的ClassLoader实例加载,那么这两个类对象也不是相等的;
- 如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类的一部分保存在方法区中;
- Java中主动使用类:
- 创建类的实例;
- 访问某个类或者借口的静态变量;
- 调用类的静态方法;
- 反射;
- 初始化一个类的子类;
- Java虚拟机启动时被表明为启动类的类;
-
java.lang.invoke.MethodHandler实例的解析结果
- 除以上7种外都是被动使用,主动与被动的区别在于类会不会被初始化;