动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

java类加载

07-21 17:47 141浏览
举报 T字号
  • 大字
  • 中字
  • 小字

 

一个Java文件从编码完成到最终执行其实是一个很简单的步骤,编译然后运行今天聊一聊java类加载不知怎的,可能是我的多愁善感,我突然觉得这和我们的人生似乎何其相似,从小学到高中我们不断的学习都是为了备战高考,然后得到我们想要的那个运行结果—心仪的大学通知书。量把关键

我们所说的类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。首先将开发者编写的 Java 源代码(.java文件)编译成 Java 字节码(.class文件),然后类加载器会读取这个 .class 文件,并转换成 java.lang.Class 的实例。有了该 Class 实例后,Java 虚拟机可以利用 newInstance 之类的方法创建其真正对象了。

java诞生的时候喊过一个振奋人心的口号:“Write Once, Run Anywhere”,为了达成这个目的,Sun 公司发布了许多可以在不同平台(Windows、Linux)上运行的 Java 虚拟机(JVM)——负责载入和执行 Java 编译后的字节码。那么这些 Java 字节码到底是什么样子呢,我们借助一段简单的代码来看一看。

源码如下:

package com.cmower.java_demo;

 

public class Test {

 

    public static void main(String[] args) {

        System.out.println("沉默王二");

    }

 

}

代码编译通过后,通过 xxd Test.class 命令查看一下这个字节码文件。

xxd Test.class

00000000: cafe babe 0000 0034 0022 0700 0201 0019  .......4."......

00000010: 636f 6d2f 636d 6f77 6572 2f6a 6176 615f  com/cmower/java_

00000020: 6465 6d6f 2f54 6573 7407 0004 0100 106a  demo/Test......j

00000030: 6176 612f 6c61 6e67 2f4f 626a 6563 7401  ava/lang/Object.

00000040: 0006 3c69 6e69 743e 0100 0328 2956 0100  .....()V..

00000050: 0443 6f64 650a 0003 0009 0c00 0500 0601  .Code...........

00000060: 000f 4c69 6e65 4e75 6d62 6572 5461 626c  ..LineNumberTabl

可能你们的第一感觉和我一样有点懵,这些字节码中的数字和字母到底代表着什么?我们着重讲一下这段字节码中的 cafe babe,它被称为“魔数”,是 JVM 识别 .class 文件的标志。文件格式的定制者可以自由选择魔数值(只要没用过),比如说 .png 文件的魔数是 8950 4e47。

  追根溯源,我们来了解一下这些class文件的来源。除了常见的开发者在应用程序中编写的位于项目目录下的类之外,还有java内部自带的核心类,java核心扩展类,以及动态加载远程的.class文件。而且,与之相对应的有不同的加载器来负责加载。

ClassLoader 是 Java 提供的类加载器,绝大多数的类加载器都继承自 ClassLoader,它们被用来加载不同来源的 Class 文件。Java 运行的基础类,由一个名为 BootstrapClassLoader 加载器负责加载,它也被称作 根加载器/引导加载器。注意,BootstrapClassLoader 比较特殊,它不继承 ClassLoader,而是由 JVM 内部实现;java 核心扩展类由ExtensionClassLoader 负责加载;开发者在项目中编写的类,这些文件将由 AppClassLoader 加载器进行加载,它也被称作 系统类加载器 System ClassLoader;如果想远程加载如(本地文件/网络下载)的方式,则必须要自己自定义一个 ClassLoader,复写其中的 findClass() 方法才能得以实现。

接下来我们来介绍一下java的类加载过程。Java 的类加载过程可以分为 5 个阶段:载入、验证、准备、解析和初始化。这 5 个阶段一般是顺序发生的,但在动态绑定的情况下,解析阶段发生在初始化阶段之后。

1)Loading(载入)

JVM在该阶段的主要目的是将字节码从不同的数据源转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象。

2)Verification(验证)

JVM会在该阶段对二进制字节流进行校验,只有符合JVM字节码规范的才能被 JVM 正确执行。该阶段是保证JVM安全的重要屏障,所以会经过严格的检查和筛选。

3)Preparation(准备)

JVM 会在该阶段对类变量(也称为静态变量,static 关键字修饰的)分配内存并初始化(对应数据类型的默认初始值,如 0、0L、null、false 等)。容易忽略的是,static final 修饰的变量被称作为常量,和类变量不同,常量一旦赋值就不会改变了。

4)Resolution(解析)

该阶段将常量池中的符号引用转化为直接引用。

符号引用以一组符号(任何形式的字面量,只要在使用时能够无歧义的定位到目标即可)来描述所引用的目标。

在编译时,Java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如 com.Wanger 类引用了 com.Chenmo 类,编译时 Wanger 类并不知道 Chenmo 类的实际内存地址,因此只能使用符号 com.Chenmo。

直接引用通过对符号引用进行解析,找到引用的实际内存地址。

5)Initialization(初始化)

该阶段是类加载过程的最后一步。在准备阶段,类变量已经被赋过默认初始值,而在初始化阶段,类变量将被赋值为代码期望赋的值。换句话说,初始化阶段是执行类构造器方法的过程。

双亲委派模型:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。

使用双亲委派模型有一个很明显的好处,那就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,这对于保证 Java 程序的稳定运作很重要。如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等——双亲委派模型能够保证同一个类最终会被特定的类加载器加载。

类加载方式是 Java 上非常创新的一项技术,给未来的热修复技术提供了可能。我们只有通过不断的学习掌握这门技术,结合其他的java开发技术,才能为java创造无限的未来。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵1280
程序员代码小兵1280

11篇文章贡献54542字

作者相关文章更多>

推荐相关文章更多>

Java数据结构

HelloWorld10-31 08:24

浅谈MySQL中SQL优化的常用方法

军哥08-12 23:29

五分钟读懂UML类图

江湖人称小李白12-10 10:41

MyBatis开发框架的四大核心

IT逐梦者08-17 21:43

一次搞定continue,break和return

HelloWorld11-06 11:19

发评论

举报

0/150

取消