前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类加载:双亲委派机制

类加载:双亲委派机制

作者头像
chenchenchen
发布2021-09-06 14:28:11
4920
发布2021-09-06 14:28:11
举报
文章被收录于专栏:chenchenchenchenchenchen
  • 类的生命周期
  • 类加载器
  • 双亲委派机制

类的生命周期

类从被加载到虚拟机开始,到卸载出内存为止,它的整个的生命周期包括:加载、连接(验证、准备、解析)、初始化、使用和卸载七个阶段

加载过程

将编译之后的Class文件加载至虚拟机,并存储在方法区。

连接过程

连接被分为了验证、准备、解析三部分。

验证:Class文件中的信息是否符合Java虚拟机的要求,是否安全(可能会做出危害虚拟机的行为)。

准备:为类变量分配内存,并设置类变量的初始值。不包含实例变量的内存,实例变量的内存将会被分配到堆中。

这里的初始值指的是数据类型的零值,并非程序员设置的值,比如static int i = 1;此时得到的i是0,赋值为1将会在初始化阶段执行。

解析:将常量池中的符号引用替换为直接引用(内存地址)。

a.run();代码表示对A类的run()方法的符号引用,由run()方法的全名 和 相关描述符组成。 解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向A类的run()方法在方法区的内存位置,这个指针就是直接引用

初始化

前面的类加载过程中,除了通过自定义类加载器参与之外,其余的动作其实都是由虚拟机主导和控制的。

初始化是类加载的最后一步,真正开始执行程序中代码的步骤,比如将i赋值为1的过程,初始化类变量,加载类的静态语句块的过程。

在准备阶段赋值过一次了,那次赋值是系统要求的初始值,跟我们个人的设定无关,而初始化的赋值才是真正根据程序员设计而主导的初始化

使用和卸载

使用类的阶段就是new对象的过程,我们会使用到各种类型对象,以及使用完毕对这个类型的卸载,卸载一般发生在程序关闭的时候。

程序正常执行结束、遇到异常关闭、或者操作系统出问题导致程序关闭这些都会卸载这些类的加载。

类加载器

从JVM的角度看,类加载器主要有两类:

  • Bootstrap ClassLoader和其他类加载,Bootstrap ClassLoader是C++语言实现,是虚拟机自身的一部分;
  • 其他类加载器都是Java语言实现,不属于虚拟机,全部继承自抽象类java.lang.ClassLoader

从Java开发者的角度看,需要了解类加载器的双亲委派模型,如下图所示:

启动类加载器Bootstrap ClassLoader

这个类加载器将负责存放在/lib目录中,并且是虚拟机会识别的jar类库加载到内存中。更直白点说,就是我们常用的java.lang开头的那些类,一定是被Bootstrap ClassLoader加载的

扩展类加载器Extension ClassLoader

它负责加载/lib/ext目录中的、或者被java.ext.dirs系统变量指定的路径中的所有类库。

应用程序类加载器Application ClassLoader

它负责加载用户CLASSPATH环境变量指定的路径中的所有类库。如果应用程序中没有自定义过自己的类加载器,这个就是一个Java程序中默认的类加载器。

用户自定义的类加载器

用户在需要的情况下,可以实现自己的自定义类加载器,一般而言,在以下几种情况下需要自定义类加载器

  • 隔离加载类。某些框架为了实现中间件和应用程序的模块的隔离,就需要中间件和应用程序使用不同的类加载器
  • 修改类加载的方式。类加载的双亲委派模型并不是强制的,用户可以根据需要在某个时间点动态加载类
  • 扩展类加载源,例如从数据库、网络进行类加载
  • 防止源代码泄露。Java代码很容易被反编译和篡改,为了防止源码泄露,可以对类的字节码文件进行加密,并编写自定义的类加载器来加载自己的应用程序的类

双亲委派机制

向上委托,向下加载

如果一个类加载器收到了类加载的请求,不考虑们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有就把请求委托给父加载器去完成,依次向上。

因此,所有的类加载请求最终都被传递到顶层的启动类加载器BootstrapClassLoader。这时候开始考虑自己是否能加载了,如果在它的搜索范围中没有找到所需的类时,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

源码

“java.lang”包下的ClassLoader类中loadClass方法代码如下:

代码语言:javascript
复制
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
//              -----??-----
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
        // 首先,检查是否已经被类加载器加载过
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                // 存在父加载器,递归的交由父加载器
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 直到最上面的Bootstrap类加载器
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
 
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClass(name);
            }
        }
        return c;
}

双亲委派机制的作用

1、避免类的重复加载。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。

2、保护程序安全,保证核心类不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

  • 自定义类:java.lang.String (没用)
  • 自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)

为了系统类的安全,类似java.lang.String这种,jvm需要保证 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。系统的类已经被Bootstrap classLoader加载过了,所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

如果我们有一个类想要通过自定义的类加载器来加载,可以通过重写ClassLoader中的loadClass方法,实现自定义类加载器。改变类的类加载器的时候要注意,如果全部改了,找不到Object.class这种顶级类,就加载不了了。

参考:

通俗易懂的双亲委派机制:https://blog.csdn.net/codeyanbao/article/details/82875064

Java 符号引用 与 直接引用:https://www.cnblogs.com/mzzcy/p/7223405.html

如何破坏双亲委派:https://mp.weixin.qq.com/s/k0_K627ENyHeE92vW5HIiw

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-05-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类的生命周期
    • 加载过程
      • 连接过程
        • 初始化
          • 使用和卸载
          • 类加载器
            • 启动类加载器Bootstrap ClassLoader
              • 扩展类加载器Extension ClassLoader
                • 应用程序类加载器Application ClassLoader
                  • 用户自定义的类加载器
                  • 双亲委派机制
                    • 向上委托,向下加载
                      • 源码
                        • 双亲委派机制的作用
                        相关产品与服务
                        消息队列 TDMQ
                        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档