JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader:
BootstrapClassLoader(启动类加载器) :最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的jar包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。
ExtensionClassLoader(扩展类加载器) :主要负责加载目录 %JRE_HOME%/lib/ext 目录下的jar包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。
AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用classpath下的所有jar包和类。
每一个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。
验证代码如下:
public class ClassLoaderDemo{
public static void main(String[] args) {
ClassLoader grantparent = ClassLoaderDemo.class.getClassLoader().getParent().getParent();
ClassLoader parent = ClassLoaderDemo.class.getClassLoader().getParent();
System.out.println("ClassLoaderDemo's classLoader is "+ClassLoaderDemo.class.getClassLoader());
System.out.println("ClassLoaderDemo's parent's classLoader is "+parent);
System.out.println("ClassLoaderDemo's grantparent's classLoader is "+grantparent);
}
}
输出结果如下:
输出结果图.png
从结果可以看出,由AppClassLoader加载自定义的类,AppClassLoader的父类是ExtClassLoader,ExtClassLoader的父类加载器为null,null并不代表ExtClassLoader没有父类加载器,而是 Bootstrap ClassLoader 。
需要注意的是,类加载器之间的“父子”关系也不是通过继承来体现的,是由“优先级”来决定。
为什么要有双亲委派模型?
为了防止恶意代码篡改Java原生的代码,比如用户自定义了一个lang.String类,又使用了这个类,那编译器也不知道到底是使用的是哪个String,所以肯定会报错。
再谈Java沙箱机制,谈到Java是安全的,是因为Java提供了一个“沙箱”机制,这个“沙箱”基本组件其中就包括类加载器。在Java沙箱中,类加载体系结构是第一道防线,可以防止而已代码去干扰正常程序代码,这是通过由不同的类装载器装入的类提供不同的命名空间来实现的。命名空间由一系列唯一的名称组成,每一个被加载的类都有不同的命名空间是由Java虚拟机为每一个类加载器维护的。
类加载器体系结构在三个方面对java的沙箱起作用
1)它防止恶意代码区干涉善意的代码
2)它守护了被信任的类库边界
3)它将代码归入保护域,该域确定了代码可以进行哪些操作。
第二道防线则是Class文件检验器
Class文件检验器保证装载的class文件内容的内部结构的正确,并且这些class文件相互协调一致。
Class文件检验器实现的安全目标之一就是程序的健壮性,JAVA虚拟机的class文件检验器要进行四趟扫描来完成它的操作。
剩下的可以参考
https://blog.csdn.net/liyazhou0215/article/details/76794480
网友评论