JVM原理之双亲委派机制

Java字节码文件经过类加载子系统加载、链接、初始化后加载到内存中。

JVM原理之双亲委派机制_第1张图片

JVM中的类加载器

JVM中类加载器分为两种引导类加载器(Bootstrap ClassLoader)和自定义类加载器。

  • 引导类加载器使用C/C++语言实现,嵌套在JVM内部,它用来加载Java核心类库(JAVA_HOME/jre/lib/rt.jar、resource.jar或者sun.boot.class.path路径下的内容),用于提供JVM自身需要的类,没有父类加载器,同时加载扩展类加载器和应用程序类加载器,并指定他们的父类加载器,处于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类;
  • 自定义类加载器,所有直接或者简介继承了java.lang.ClassLoader类的加载器。

类的主动使用和被动使用

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

主动使用:

  • 创建类实例;
  • 访问某个类或者接口的静态变量,或者对该静态变量赋值;
  • 调用类的静态方法;
  • 反射(例如,Class.forName(“jack.test”));
  • 初始化一个类的子类;
  • Java虚拟机启动时被标记为启动类的类;
  • JDK 7 中开始提供的动态语言支持:
    java.lang.invoke.MethodHandler实例的解析结果

除了上述7中情况,其他使用Java类的方式被看作是对类的被动使用,都不会导致类的初始化。

双亲委派机制工作原理

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;

如果父类加载器还存在其父类加载器,则进一步向上委托,依次向上,请求最终将到达顶层的启动类加载器;

如果夫类加载器可以完成类的加载任务,就成功返回;否则,子加载器才会尝试自己去加载。

JVM原理之双亲委派机制_第2张图片

双亲委派机制优势

  • 避免类的重复加载;
  • 保护程序安全,防止核心API被随意篡改(沙箱安全机制,保护java核心源代码)

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

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

举例分析

假如我们自定义一个String类,和java中自带的String的包名在一个位置,如下所示:

package java.lang;

public class String {
    static {
        System.out.println("hello jacky");
    }
}

那么,当我们在另一个包下引用这个String类时候,系统会默认先委托给父类的加载器,一直找到JVM自带的启动类加载器。因此,下边的代码不会加载我们自定义的(仿冒的)String类。

package com.jack;

public class ClassLoaderTest {
    public static void main(String[] args) {
        String str = new String();
    }
}

另外,程序入口类的包名不能指定为java.lang,否则会抛出java.lang.SecurityException: Prohibited package name: java.lang异常。

JVM原理之双亲委派机制_第3张图片

因为java.lang包下的类需要通过引导类加载器来加载,为了防止恶意加载,JVM会提示权限不足。

你可能感兴趣的:(JVM原理)