· 双亲委派机制、沙箱安全机制是JVM中类加载器系统的相关术语
· 在这之前,应该先了解JVM类加载器系统的相关概念
一、类加载器基础知识
见下图1,java文件首先会被编译成class文件,class文件作为Java虚拟机的可识别文件,编译完成后,需要加载到内存。
我们针对不同的class文件,会相应用到不同的类加载器。
· 引导类加载器:Bootstrap ClassLoader,也叫做启动类加载器/根类加载器,并不继承ClassLoader类。引导类加载器使用C/C++语言实现,在Java创立初期,C/C++在速度上占据优势,所以引导类加载器嵌套在JVM内部,用来加载Java核心类库,为Java加速。
· 扩展类加载器:Extension ClassLoader,继承了ClassLoader类。上层类加载器为引导类加载器。负责加载JRE的扩展目录,从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK系统安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的jar放在此目录下,也会自动由扩展类加载器加载。
· 系统类加载器:Application ClassLoader,也叫做应用程序类加载器,继承了ClassLoader类。系统类加载器主要加载日常开发中开发者自己编写的类。
· 自定义类加载器(狭义):User-Defined ClassLoader,继承了ClassLoader类。狭义上的自定义类加载器,是开发人员自定义的一类加载器。但是从广义上来说,官方在Java虚拟机中规定将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。
所以说(参考上图1),扩展类加载器、系统类加载器、自定义类加载器(狭义)都算作自定义类加载器。
注意,各个加载器是没有继承关系的,途中的箭头只是逻辑上的一种理解。
二、双亲委派机制
核心解释:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
举个例子:
假设我们在项目工程中写一个类叫做java.lang.Integer
,此类在加载的时候,由于是我们自己写的,它对应的类加载器是系统类加载器,然后由于双亲委派机制,向上沿此路线传递:系统类加载器 --> 拓展类加载器 --> 启动类加载器,到了启动类加载器的时候,加载了核心类库中的java.lang.Integer
。这样就防止了核心类被篡改,也可以防止类被重复加载。
得出双亲委派机制的优点:
由上例:
1、安全,可避免用户自己编写的类动态替换Java的核心类,如java.lang.String。,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
2、避免全限定命名的类重复加载(使用了findLoadClass()判断当前类是否已加载)。Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次
三、沙箱安全机制
核心解释:
主要用来防止恶意代码污染java源代码。
自定义String类,但是在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class),报错信息说没有main方法就是因为加载的是rt.jar包中的String类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制。