类加载器子系统的作用
- 从文件系统或者网络中加载
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放在上面的目录,也会自动由扩展类加载器加载;
-
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
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种外都是被动使用,主动与被动的区别在于类会不会被初始化;
网友评论