JVM类加载中的双亲委派机制

【1】什么是双亲委派

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

工作原理

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

【2】双亲委派机制举例

如下所示,我们想要加载jdbc.jar中的类。

  • 加载jdbc.jar 用于实现数据库连接的时候会首先去java.sql下加载需要的类和接口
  • 双亲委派给引导类加载器加载需要的类和接口
  • 部分实现类是第三方厂商提供故而反向委派给系统类加载器加载

【3】沙箱安全机制

我们首先自定义一个java.lang.String ,编写main方法进行测试:

java 复制代码
public class String {
    static{
        System.out.println("我是自定义的String类的静态代码块");
    }
    //错误: 在类 java.lang.String 中找不到 main 方法
    public static void main(String[] args) {
        System.out.println("hello,String");
    }
}

会报错如下:

java 复制代码
错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
   public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application

解释: 采用双亲委派机制,引导类加载器会加载核心类库中的String(rt.jar包中java\lang\String.class),其是没有main方法的。

那如何定义一个核心类库不存在的类呢,比如下面示例:

java 复制代码
public class Jane {

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

将会报错如下: 也就是不允许你使用java.lang这个包名。

java 复制代码
java.lang.SecurityException: Prohibited package name: java.lang
	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:655)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:754)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" 

上面这种保护机制就是沙箱安全机制,可以保护java核心类库。

【4】双亲委派机制的优势

通过上面的例子,我们可以知道,双亲机制可以

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改
    • 自定义类:java.lang.String
    • 自定义类:java.lang.Jane(报错:阻止创建 java.lang开头的类)

【5】如何判断两个class对象是否相同

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

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

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

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

【6】类的主动使用和被动使用

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

主动使用,又分为七种情况:

  • 创建类的实例
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(比如:Class.forName("XXXXXX"))
  • 初始化一个类的子类
  • Java虚拟机启动时被标明为启动类的类
  • JDK7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

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

相关推荐
程序员志哥6 小时前
JVM系列(十三) -常用调优工具介绍
jvm
后台技术汇6 小时前
JavaAgent技术应用和原理:JVM持久化监控
jvm
程序员志哥7 小时前
JVM系列(十二) -常用调优命令汇总
jvm
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭7 小时前
聊聊volatile的实现原理?
java·jvm·redis
_LiuYan_10 小时前
JVM执行引擎JIT深度剖析
java·jvm
王佑辉10 小时前
【jvm】内存泄漏的8种情况
jvm
工业甲酰苯胺10 小时前
JVM简介—1.Java内存区域
java·jvm·python
yuanbenshidiaos1 天前
c++---------数据类型
java·jvm·c++
java1234_小锋1 天前
JVM对象分配内存如何保证线程安全?
jvm
40岁的系统架构师1 天前
1 JVM JDK JRE之间的区别以及使用字节码的好处
java·jvm·python