JVM基础篇——双亲委派机制和沙箱安全机制

双亲委派机制

双亲委派机制

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由收类处理,它是一种任务委派模式。

我们现在想出一个问题,如果我们也一个java.lang.String类,那么进行类加载的时候加载是哪一个类呢??

先写段代码

# 自定义String类
package java.lang;

public class String {

    static {
        System.out.println("自定义String");
    }

    public static void main(String[] args) {
        System.out.println("hello , world");
    }
}

# String测试类  StringTest.java
public class StringTest {
    public static void main(String[] args) {
        java.lang.String string = new java.lang.String();
    }
}

测试的结果是什么都不输出,所以说明使用的是核心api的Stribg类

为什么会出现这样的结果呢??那么进入咱们今天的主题——双亲委派机制

双亲委派机制工作原理

(1)如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行
(2)如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达项层的启动类加载器
(3)如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

那么刚才那个类的加载流程是什么样的呢??

流程图理解:
在这里插入图片描述

首先走的是系统类加载器,之后发现还有父类,去找扩展类加载器,发现还有父类,去找引导类加载器。如果说最上面的引导类加载器不负责这个类的加载,那么扩展类加载器负责加载,如果扩展类加载器不负责加载,那么系统类(应用程序类)加载器负责加载。

之前的类引导子系统提到过:引导类加载器加载的是Java核心类库JAVA_HOME/jre/lib/rt,jar、resources.jar、sun.boot.class.path路径下的内容,只加载包名是java, javax, sun开头类。
那么引导类加载器负责java.lang包下类的加载,所以加载的是java核心类库的String类。

双亲委派机制的优点
  • 防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
  • 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

看下面这段代码:

# 自定义java.lang.String类
package java.lang;
public class String {
    static {
        System.out.println("自定义String");
    }
}
# 自定义java.lang.StringTest类
package java.lang;
public class StringTest {
    public static void main(String[] args) {
        String string = new String();
    }
}

# 执行时期抛出异常
java.lang.SecurityException: Prohibited package name: java.lang

在私自创建java类的时候,可能和已经存在的类发生冲突,甚至对程序产生损害,Java采取双亲委派机制,保证类只是加载一次,可以避免类的重复加载,保证程序的安全。

沙箱安全机制

自定义String类,在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件。(rt. jar包中java\lang\String. class),报错信息说没有main方法p就是因为加载的是rt. jar包中的String类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制。

其他的类加载器知识点补充

对类加载器的引用

JVM必须知道–个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么JVM会将这。个类加载器的-一个引用作为类型信息的一部分保存 在方法区中q当解析一个类型到另一个类型的引用的时候,JVM需 要保证这两个类型的类加载器是相同的。

在JVM中表示两个class对象是否为同-一个类存在两个必要条件:

  • 类的完整类名必须一致,包括包名。
  • 加载这个类的ClassLoader (指ClassLoader实例对象)必须相同。

换句话说,在JVM中,即使这两个类对象(class对象)来源同-一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的。

类的主动使用和被动使用

Java程序对类的使用方式分为:主动使用和被动使用。

  • 主动使用,又分为七种情况:
    • 创建类的实例
    • 访问某个类或接口的静态变量,或者对该静态变量赋值
    • 调用类的静态方法
    • 反射(比如: Class . forName (“com. atguigu . Test”) )
    • 初始化一个类的子类
    • Java虚拟机启动时被标明为启动类的类
    • JDK 7开始提供的动态语言支持:
      java.lang.invoke.MethodHandle实例的解析结果
      REF_getStatic、REF_putStatic、REF_ invokeStatic句柄对应的类没有初始化,则初始化
  • 除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化。

两种类的加载机制就介绍到这儿,谢谢你可以看到最后。Thank You!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章