类加载过程

Posted 小布丁value

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载过程相关的知识,希望对你有一定的参考价值。

#@TOC

类的初始化顺序

类加载机制

定义

在了解 Java 类加载器之前我们先谈谈什么是类加载机制:
Java 虚拟机一般使用 Java 类的流程
1.首先将开发者编写的Java源代码(.java 文件)编译生成 Java 字节码文件 (.class 文件),
2.然后类加载器会读取字节码文件,并转换成 java.lang.Class 对象
3.有了该 Class 对象后,Java 虚拟机 可以利用反射方法创建其真正的对象了。
4.Java提供的类加载器绝大多数都继承自 ClassLoader 类,它们被用来加载不 同来源的字节码文件。
思考:当类加载器要加载一个字节码文件时,它是如何判别此目标文件是一个字节码文件呢?可以通过后缀名对目标 文件进行识别吗?Of Course Not!!(试将一个.mp3 音乐文件的后缀名进行修改之后,你会发现,音乐依然可以正常 播放)。那么类加载器又是如何辨别一个文件是字节码文件呢? 我们来简单了解一下字节码的文件格式,用 javap -verbose 文件名
可以查看字节码文件格式内容。

上图存在 MD5 checksum 这个标识。MD5 checksum 的作用主要是用来进行文件的校验。MD5 checksum 后的长 串数字是经过 MD5 加密之后呈现的。那么 MD5 又是对哪些内容进行加密后得到长串数字的呢?字节码文件的前 4 个字节我们将其称为魔数(0xCAFEBABE),魔数的作用就是用来标记字节码文件的类型,MD5 就是对专门用来标记 字节码文件类型的魔数进行加密操作的。 类加载器在识别字节码文件之后,紧接着就进行字节码文件的加载工作。JDK 提供了多种类加载器,那么究竟 由哪一种类加载器对此字节码文件进行加载呢?


思考:为什么加载 String 类和 ClassLoaderTest 类的类加载器不是同一种类加载器呢? 下面我们就来了解一下 JDK 提供的类加载器,同时解释一下运行结果。 主要介绍一下 JDK 提供的三种类加载器,分别是 Bootstrap ClassLoader(启动类加载器),ExtClassLoader(扩展类加 载器),AppClassLoader(应用类加载器)。
每一种类加载器都有其指定的类加载路径。 (1) Bootstrap ClassLoader(启动类加载器又称为根加载器/引导类加载器)主要加载 JAVA_HOME/jre/lib 里的 jar 包,该目录下的所有 jar 包都是运行 JVM 时所必需的 jar 包。注意:类加载器其实自身也是一个 Java 类,因此, 自身类加载器需要被其他类加载器进行加载后方可使用,显然必须有一个类加载器的顶级父类(也就是 Bootstrap ClassLoader,该类加载器是由 C 语言代码进行开发的)是其他类加载器的父类。关键点在于,如果一个类的类加载器 是 Bootstrap ClassLoader,那么该类的 getClassLoader()方法返回 null。 (2) ExtClassLoader 主要加载 Java 核心扩展类,即 JAVA_HOME/jre/ext 目录下的 jar 文件。
(3) AppClassLoader 主要加载的是开发者在应用程序中编写的类,即 CLASSPATH 路径下所有的 jar 文件。 那么这三种类加载器之间的关系又是怎样的呢

既然我们对类的加载范围有了初步的了解,那么如果出现下述情况,你能准确分析其产生的原因吗? 问题:将当前用户自定义的 People 类所在的源文件以 jar 包的形式存放到 ExtClassLoader 加载类路径下,我们 调用 People 类的 getClassLoader()方法,返回的是 AppClassLoader 类加载器还是 ExtClassLoader 类加载器呢?还是说 两者都返回呢? 如果对这个问题心里没有准确的答案的话,不妨继续向下研究吧! 首先,我们来定义一个普通的 People 类,获取加载 People 类的类加载器


这就发现 People 类加载器变成了 ExtClassLoader,那么为什么不继续返回 AppClassLoader 呢? 这是因为类加载器之间采用的是双亲委派模型的加载方式进行类加载的。 那么问题又来了,什么是双亲委派模型呢?

双亲委派模型


上图中展示的类加载器之间的层次关系,就称为类加载器的双亲委派模型。 双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应该有自己的父类加载器。
双亲委派模型的工作过程如下
(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。
(2)如果没有找到,就去委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载过的 类中是否包含这个类,有就返回,没有就委托其父类去加载,直到委托到启动类加载器为止。因为如果父类加载器为空了,就代表使用启动类加载器作为父加载器去加载该类。(也就是看到的 String 类加载器为 null)
(3)如果启动类加载器加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用 AppClassLoader 来 加载,继续失败就会抛出一个异常 ClassNotFoundException。 使用双亲委派模型的好处:
(1)安全性,避免用户自己编写的类动态替换 Java 的一些核心类。如果不采用双亲委派模型的加载方式进行 类的加载工作,那我们就可以随时使用自定义的类来动态替代 Java 核心 API 中定义的类。例如:如果黑客将“病毒 代码”植入到自定义的 String 类当中,随后类加载器将自定义的 String 类加载到 JVM 上,那么此时就会对 JVM 产生 意想不到“病毒攻击”。而双亲委派的这种加载方式就可以避免这种情况,因为 String 类已经在启动时就被引导类 加载器进行了加载。
(2)避免类的重复加载,因为 JVM 判定两个类是否是同一个类,不仅仅根据类名是否相同进行判定,还需要判断加载该类的类加载器是否是同一个类加载器,相同的 class 文件被不同的类加载器加载得到的结果就是两个不 同的类。

产生字节码文件

命令


魔术

主次版本号

常量

类加载过程

1.类的装载阶段

双亲委派模型

这就完美了解释了虽然生成了两个对象,但类加载只产生了一个Class对象

2.类的链接阶段

验证阶段 文件格式的验证 jdk 版本号验证

准备阶段 给静态变量开辟内存 ,赋类型初值
static int a=10;//这一步值是0;
解析阶段 符号引用 解析为 直接引用过程
Strint str =“hell0”;
1.在磁盘上编译生成字节码文件,str保存的是磁盘上的地址,成为符号引用
2,通过类加载机制加载到JVM虚拟机内存上,就是直接引用
操作系用上 从逻辑地址转变为物理地址的过程

3.类的初始化阶段

专门给静态变量赋值操作
a=10;

以上是关于类加载过程的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

如何通过单击适配器类中代码的项目中的删除按钮来删除列表视图中的项目后重新加载片段?

solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例

类加载过程(clinit()),对象实例化过程(init())

类加载过程