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

类加载过程,双亲委派模型?

作者头像
袁新栋-jeff.yuan
发布2020-08-26 18:08:21
2940
发布2020-08-26 18:08:21
举报

背景

java通过字节码和JVM机制,提供了强大的跨平台能力,理解Java的类加载机制能让我们更加了解java的运行过程

为什么要进行类加载?

  1. 我们所写的java代码是我们人能看懂的,但是计算机并不认识它,所以我们就得把它进行转换,首先第一步就是得将我们所编写的 Java文件编译为class文件,然后通过我们的类加载过程将.class文件加载到内存且转换为计算机可以认识的语言。

类加载过程?

  1. 类加在过程分为三个大阶段和五个小阶段。
在这里插入图片描述
在这里插入图片描述
加载阶段(Loading)

它是 Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 对象),这里的数据源可能是各种各样的形态,如 jar 文件、class 文件,甚至是网络数据源等;如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。

代码语言:javascript
复制
(1)文件格式的验证:验证.class文件字节流是否符合class文件的格式的规范,并且能够被当前版本的虚拟机处理。这里面主要对魔数、主版本号、常量池等等的校验(魔数、主版本号都是.class文件里面包含的数据信息、在这里可以不用理解)。

(2)元数据验证:主要是对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求,比如说验证这个类是不是有父类,类中的字段方法是不是和父类冲突等等。

(3)字节码验证:这是整个验证过程最复杂的阶段,主要是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。在元数据验证阶段对数据类型做出验证后,这个阶段主要对类的方法做出分析,保证类的方法在运行时不会做出威海虚拟机安全的事。

(4)符号引用验证:它是验证的最后一个阶段,发生在虚拟机将符号引用转化为直接引用的时候。主要是对类自身以外的信息进行校验。目的是确保解析动作能够完成。
对整个类加载机制而言,验证阶段是一个很重要但是非必需的阶段,如果我们的代码能够确保没有问题,那么我们就没有必要去验证,毕竟验证需要花费一定的的时间。当然我们可以使用-Xverfity:none来关闭大部分的验证。
链接(Linking)

这是核心的步骤,简单说是把原始的类定义信息平滑地转化入 JVM 运行的过程中。这里可进一步细分为三个步骤:

验证(Verification)

这是虚拟机安全的重要保障,JVM 需要核验字节信息是符合 Java 虚拟机规范的,否则就被认为是 VerifyError,这样就防止了恶意信息或者不合规的信息危害 JVM 的运行,验证阶段有可能触发更多 class 的加载。

准备(Preparation)

创建类或接口中的静态变量,并初始化静态变量的初始值。但这里的“初始化”和下面 的显式初始化阶段是有区别的,可以理解为半初始化状态, 侧重点在于分配所需要的内存空间,不会去执行更进一步的 JVM 指令,比如

代码语言:javascript
复制
public static int value = 1;

在这里准备阶段过后的value值为0,而不是1。赋值为1的动作在初始化阶段。 当然还有其他的默认值。 注意,在上面value是被static所修饰的准备阶段之后是0,但是如果同时被final和static修饰准备阶段之后就是1了。我们可以理解为static final在编译器就将结果放入调用它的类的常量池中了。

解析(Resolution)

在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。

代码语言:javascript
复制
(1)符号引用:以一组符号来描述所引用的目标,可以是任何形式的字面量,只要是能无歧义的定位到目标就好,就好比在班级中,老师可以用张三来代表你,也可以用你的学号来代表你,但无论任何方式这些都只是一个代号(符号),这个代号指向你(符号引用)

(2)直接引用:直接引用是可以指向目标的指针、相对偏移量或者是一个能直接或间接定位到目标的句柄。和虚拟机实现的内存有关,不同的虚拟机直接引用一般不同。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

初始化阶段(initialization)

这一步真正去执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。

什么是双亲委派模型?

理解双亲委派模型
  • 感觉这个字面意思好难理解, 个人理解:有一件事我不敢让父亲去干,但是我父亲默认也不干,于是让父亲的父亲去干,父亲的父亲也不干(也就是你爷爷),你爷爷也不干,让你爷爷的父亲去干于是你爷爷的父亲也没办法推辞的上面没人了。只好自己干了。但是有一种可能就是你爷爷的父亲干不了这件事,于是又往下推谁能干谁就干。
在这里插入图片描述
在这里插入图片描述
  • 上面的过程中是如何来确定自己无法加载呢? 1)如上图,其中每个类加载气都有自己对应加载的目标,比如说我们的Object,它是存放在rt.jar之中的,无论哪个类加载器要加载这一个类,最终都会委派到最顶端的启动类加载器,因此Object类在程序的各种类加载器环境中都是同一个类,就算是你在classpath去写一个Object对象,到最后也会加载得失rt.jar中的Object对象。这也就体现了双亲委派要解决的问题之一,就是防止重复加载导致冲突。导致程序一片混乱。
四种类加载器的介绍
  • Bootstrap ClassLoader:启动类加载器,这个类加载器将负责存放在<JAVA_HOME>/lib目录中、被-Xbootclasspath参数所指定的路径中,并且是虚拟机会识别的jar类库加载到内存中。更直白点说,就是我们常用的java.lang开头的那些类,一定是被Bootstrap ClassLoader加载的。
  • Extension ClassLoader:扩展类加载器,这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HONME>/lib/ext目录中的、或者被java.ext.dirs系统变量指定的路径中的所有类库。
  • Application ClassLoader:应用程序类加载器,这个类加载器由sun.misc.Launcher$AppClassLoader实现,它负责加载用户CLASSPATH环境变量指定的路径中的所有类库。如果应用程序中没有自定义过自己的类加载器,这个就是一个Java程序中默认的类加载器。
  • 用户自定义的类加载器:用户在需要的情况下,可以实现自己的自定义类加载器,一般而言,在以下几种情况下需要自定义类加载器: (1)隔离加载类。某些框架为了实现中间件和应用程序的模块的隔离,就需要中间件和应用程序使用不同的类加载器; (2)修改类加载的方式。类加载的双亲委派模型并不是强制的,用户可以根据需要在某个时间点动态加载类; (3)扩展类加载源,例如从数据库、网络进行类加载; (4)防止源代码泄露。Java代码很容易被反编译和篡改,为了防止源码泄露,可以对类的字节码文件进行加密,并编写自定义的类加载器来加载自己的应用程序的类。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 为什么要进行类加载?
  • 类加载过程?
    • 加载阶段(Loading)
      • 链接(Linking),
        • 验证(Verification),
          • 准备(Preparation),
            • 解析(Resolution),
              • 初始化阶段(initialization),
              • 什么是双亲委派模型?
                • 理解双亲委派模型
                  • 四种类加载器的介绍
                  相关产品与服务
                  消息队列 TDMQ
                  消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档