java虚拟机-双亲委派机制

本文详细介绍了Java中的类加载器,包括启动类加载器、拓展类加载器和应用程序类加载器,并阐述了双亲委派机制的重要性,如沙箱安全和避免重复加载。同时,讨论了自定义类加载器的应用场景,如类的加解密和热部署,并提及如何破坏双亲委派机制以实现特定需求。


站在java开发人员的角度来看,类加载器可以划分为三层类加载器和双亲委派架构。

一、三层类加载器

三层类加载器是指启动类加载器、拓展类加载器、应用类加载器。首先看看三层类加载器的结构
在这里插入图片描述

1.启动类加载器

  • 启动类加载器(Bootstrap Class Loader)由C++实现,负责加载<JAVA_HOME>\lib 或者 -Xbootclasspath 下的内容。
  • 启动类加载器是我们不能直接用Java代码引用的,但从java.lang.ClassLoader.loadClass()方法中可以看到,只要parent为null,就会委托给启动类加载器去加载。所以如果我们自己定义了一个类加载器并且想要委派给引导类加载器去处理的话,只需要把parent设置为null就行了
Class<?> c = findLoadedClass(name);
if (c == null) {
    long t0 = System.nanoTime();
    try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
        	// parent为null 委托给启动类加载器去加载
            c = findBootstrapClassOrNull(name);
        }
    } catch (ClassNotFoundException e) {
        // ClassNotFoundException thrown if class not found
        // from the non-null parent class loader
    }
    ...
}

java8 文档中也说明了用null来代表引导类加载器:

Returns the parent class loader for delegation. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class loader’s parent is the bootstrap class loader.

2.拓展类加载器

  • 拓展类加载器(Extension Class Loader)负责加载<JAVA_HOME>\lib\ext 以及 java.ext.dirs 指定目录下的类

3.应用程序类加载器

  • 应用程序类加载器(Application Class loader)也叫系统类加载器,加载ClassPath 下的类

二、双亲委派机制

使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。

1.双亲委派机制的作用

在jvm中,每一个类加载器都有一个独立的类名称空间,比较两个类是否相等,必须是在同一个类加载器的前提下才有意义。如果没有双亲委派机制,对于java.lang.Object类,如果用不同类加载器去加载的话,系统中就会出现多个Object类,这样整个程序就乱套了。具体来说,双亲委派要做的就是:

  • 沙箱安全机制:如果有人想替换系统级别的类:String.java是不会被加载的,这样便可以防止核心API库被随意篡改。
  • 避免类的重复加载:当父加载器已经加载了该类时,就没有必要子加载器再加载一次,保证被加载类的唯一性。

双亲委派机制要做的就是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父类加载器去完成。

2.自定义类加载器

如果我们要自定义类加载器的话,只需要重写ClassLoader的findClass方法就好了,这个方法可以分为两步:

  • 读取class文件到字节数组中。在这个过程我们可以实现字节码文件解密
  • 用defineClass方法完成类加载

自定义类加载器的应用:

  1. 实现类的加解密
  2. 拓展加载源。加载非classpath下的类
  3. 隔离加载类,把类加载到不同的环境中。比如自定义类加载器加载应用jar包,这样就不会影响到中间件运行时使用的jar包
  4. 实现热部署。java中类被加载后一般是很难卸载的,要实现热部署需要利用每个类加载器类命名空间是独立的这个特点。我们可以创建守护线程监听源文件是否被修改,一旦修改的话就新建一个类加载器去重新加载类,然后用这个新的类去创建对象,这样就实现了热部署。

3.破坏双亲委派机制

双亲委派机制保证了越基础的类由越上层的加载器进行加载,但有时候基础类型又要回调用户的代码,比如JDBC。java引入了线程上下文类加载器(Thread Context ClassLoader)来加载用户代码。

此外,像刚才说的热部署的实现,为了能重新加载类我们是不想向上委派的(不想委派给AppClassLoader),这时候我们可以直接重写java.lang.ClassLoader.loadClass方法,不要向上委派就好了(当然也可以在findClass方法中把parent设为null,毕竟引导类加载器是找不到我们自己写的代码的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值