jvm类加载过程_类加载过程【JVM:类加载机制深度剖析】 第4篇

 本文在文章底部有配套学习视频 

1506983649b3129e033ea47da443730d.gif

前言

       在前面我们介绍了JVM的核心三部分:类加载器、字节码解释器、运行时数据区。

       这一节我们来介绍下类加载器。

一、类加载过程

ab1269772f9229aa8f242db85dcfad66.png

多个java文件经过编译打包生成可运行jar包,最终由java命令运行某个主类的main函数启动程序,这里首先需要通过类加载器把主类加载到JVM,主类在运行过程中如果使用到其它类,会逐步加载这些类。

注意:对于我们自己写的jar包里的类不是一次性全部加载的,是使用到时才加载。

类加载到使用整个过程有如下几步:

加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载 :

5cf71505cf496b4e1ba0ac34aeec87e1.png

(1)加载:在硬盘上查找并通过IO读入字节码文件,将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口。

注意:使用到类时才会加载,例如调用类的main()方法,new对象等。

(2)验证:验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求,确保不会危害虚拟机安全(验证字节码的正确性)。

(3)准备:为类的静态变量(static filed)在方法区分配内存,并赋默认初值(0值或null值)。如static int a = 100 ;静态变量a就会在准备阶段被赋默认值0。

对于一般的成员变量是在类实例化时候,随对象一起分配在堆内存中。

另外,静态常量(static final filed)会在准备阶段赋程序设定的初值,如static final int a = 666;  静态常量a就会在准备阶段被直接赋值为666,对于静态变量,这个操作是在初始化阶段进行的。

(4)解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用(将类的二进制数据中的符号引用换为直接引用)。

(5)初始化:类的初始化的主要工作是为静态变量赋程序设定的初值。

如static int a = 100;在准备阶段,a被赋默认值0,在初始化阶段就会被赋值为100。

说明:而类的实例化是指在类完全加载到内存中后,创建对象的过程

二、一个小栗子来理解类类加载过程

       通过概念,我们对于类加载过程,可能还是有点不是很懂,接下来我们来看下一个类。

2.1 编写一个Math.java代码

       编写一个简单的Math.java代码如下:

public class Math {    public static int initData = 666;    public int compute(){//        int a = 1;        int b = 2;        int c = (a+b)*10;        return c;    }    public static void main(String[] args) {        Math math = new Math();        int rs = math.compute();        System.out.println(rs);    }}

说明:上面这个代码很简单,就是定义了静态变量initData和compute()和main方法。

2.2 编译源码为字节码文件Math.class

       这个我们使用的IDE会自己进行编译成Math.class文件,我们自己也可以使用命令编译javac Math.java。

2.3 执行字节码文件

      使用java Math.class就可以执行字节码文件了,最终会输出一个结果,但在执行之前会有一个我们上面讲到的类加载过程。

2.4 第一步:加载

       Math.class是在我们的磁盘上的,所以类加载器需要通过IO从磁盘读取Math.clss字节码文件,将Math.class字节码文件加载到内存,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个。

2.5 第二步:验证

       这个主要是验证字节码文件的正确性。

       我们打开这个文件看下,我们可以使用一些工具打开Math.clss,注意使用十六进制的进行查看,要是直接打开是二进制文件,你是看不懂的哦。

在Mac下可以使用Hex Fiend打开二进制文件,下载地址:

     https://github.com/ridiculousfish/HexFiend/releases

     另外一种简单的方式就是使用vi进行打开(这是Mac哦),主要两步操作(相信玩Mac的都能看得懂):

(1)首先以二进制方式编辑这个文件

vi -b Math.clss

(2)使用xxd转换为16进制

:%!xxd

(window可以使用Notepad++,需要安装一个HEX-Editor插件)

9d142d3b059f976d3443a955e5b2de4e.png

这些十六进制的数据都是有意义的,是不能随意篡改的。

比如:

cafebabe(魔数) : 代表该文件是一个字节码文件,我们平时区分文件类型都是通过后缀名来区分的,不过后缀名是可以随便修改的

画外音:魔数这个词在不同领域代表不同的含义。在计算机领域,魔数有两个含义,一指用来判断文件类型的魔数;二指程序代码中的魔数,也称魔法值。

0000 0034:紧跟着魔数后面的4位就是版本号了,同样也是4个字节,其中前2个字节表示副版本号,后2个字节,表示主版本号。

前面两个字节是0x0000,也就是其值为0;

后面两个字节是0x0034,也就是其值为52.

所以上面的代码就是52.0版本来编译的,也就是jdk1.8.0。

002f:常量池计数器,0x002f 其值就是 47

在线进制转换工具:

https://www.sojson.com/hexconvert/64to10.html

cdc4be60a6904f149ab8b1942ec583c9.png

57f9cddcc066c402855f91b71b8535d2.png

2.6 第三步:准备

       这一步就是分配内存空间、赋予默认值,相对于我们的Math.java代码就是给:

initData分配内存空间,并且赋予默认值,此时initData=0。

2.7 第四步:解析

       将符号引用替换为直接引用。这个部分具体比较复杂,我们在后续会进行展开讲解。

2.8 第五步:初始化

对类的静态变量初始化为指定的值,执行静态代码块等。那么我们的Math中的initData对象此时数据就是666。

相关历史文章(阅读本文之前,您可能需要先看下之前的系列?)

JVM内存模型和性能调优:为什么要学习JVM - 第1篇

什么是Java虚拟机【JVM:基础入门】 - 第2篇

一图了解JVM核心组成【JVM:基础入门】 - 第3篇

JVM内存模型和调优实战课程

5cc8fd21b22ce361f5c9f352b2f40e79.png

http://t.cn/A6wWMVqG

点击「阅读原文」快速查看学习:

学习的天平上,你在左盘付出的越多,那么在右盘得到的也会随着你的付出而增多。致敬大师,致敬未来的你。

1564befe658d799cda076f8ab8c7e088.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值