欢迎访问我的博客,同步更新: 枫山别院 今天给大家分享下Java Class字节码文件的结构解析。(文末附有详细大图)

代码

首先我们创建一个Java类,然后添加一些成员变量和方法,如下:

public class Test {

String name = "hello";

int size = 10;

long num = 100;

Double pi = 3.14;

public static void main(String[] args) {

Test test = new Test();

test.print();

}

public void print() {

System.out.print(name);

}

}

大家可以看到,这是一个非常简单的类,仅仅有几个基本类型的成员变量和两个简单的方法。这是为了我们分析Class文件方便起见,没有添加复杂的结构。

编译

然后大家可以用javac命令编译一下这个类。编译之后,我们就得到了一个class文件,用sublime之类的软件打开这个class文件,我们可以看到如下信息:

CA FE BA BE 00 00 00 34 00 40 0A 00 11 00 29 08

00 2A 09 00 0C 00 2B 09 00 0C 00 2C 05 00 00 00

00 00 00 00 64 09 00 0C 00 2D 06 40 09 1E B8 51

..........

00 00 00 0C 00 01 00 00 00 0B 00 1F 00 20 00 00

00 01 00 27 00 00 00 02 00 28

是一行一行的十六进制的字符,每两个字符是一个字节,每个字节之间用空格分隔,我省略掉了中间的多行数据,如果要看完整数据,请看文末。

这些就是编译之后的信息了,文件中的字符在我们看来只是一行一行字符,其实是划分格式的。我们来看一下是如何划分的。

结构

Class文件的结构,是一个结构体,有以下元素,描述如下:

ClassFile {

u4 magic; //魔数

u2 minor_version; //Java的次版本号

u2 major_version; //Java的主版本号

u2 constant_pool_count; //常量池的长度

cp_info constant_pool[constant_pool_count]; //常量池数组

u2 access_flags; //类访问标志

u2 this_class; //类名索引

u2 super_class; //父类名索引

u2 interface_count; //实现的接口的数量

u2 interfaces[interface_count]; //接口数组

u2 fields_count; //字段数量

field_info fields[fields_count]; //字段数组

u2 methods_count; //方法数量

method_info methods[methods_count]; //方法数组

u2 attributes_count; //属性数量

attribute_info attributes[attributes_count]; //属性数组

}

左边u2,u4都是表示字节的长度,u2是两字节,u4是四字节。右边是元素的名称。

Class文件就是由以上元素,一个紧挨一个组成的。总体的结构还是不复杂的,开头的三个值,magic,minor_version,major_version和中间的access_flags,this_class,super_class,他们是固定的位置,固定的字节长度。其他的数量不一定的值,都是一个长度,然后后面就是紧跟着该长度的数组,存储着该组值。数组结构通常都有下一级的子结构。

我把字节文件按照上面的元素格式,以不同的颜色划分了出来,请大家看下结构图:

class-simple.png

看图非常的一目了然,读两遍书,不如看一遍图啊。

连续的相同颜色的格子,是同一种元素的值。元素之间是紧紧的排列在一起,大家可以看到结构非常的紧凑,节省空间。看不清的同学可以下载下来,放大一下看。

分析

1. magic 魔数

我们知道有时候通过后缀识别文件是不准确的,因为很容易就改掉后缀。所以很多文件格式,在文件的开头写上几个固定的值,为了识别方便该种格式。比如PDF文件的开头是“%PDF”,这个固定的值,就叫魔数,其实就是个标识。class文件的魔数就是“CA FE BA BE”,JVM读取开头的四个字节,如果是这个值,那么就认为这个文件是个class。

magic.jpg

2. version 版本号

魔数之后,是版本号,大家可以看到,我们有两部分的版本号。第一个版本号就是次版本号,第二个是主版本号。比如52.0,52是主版本号,0是次版本号。这个版本是为了让JVM识别编译这个clas文件的Java版本,比如Java SE8,对应的版本号是52.0,Java SE7 是51.0,Java SE6 是50.0。如果一个最高只支持Java SE7版本的JVM,读取到一个52.0,那么它可能是执行不了这个class的,以为它是Java SE8编译出来的,可能用了JavaSE8的新特性。

version.jpg

3. constant_pool 常量池

常量池占用了class中非常多的空间,存放着非常多的信息,包括数字,字符串,类和接口的名字,字段和方法的名字等等。上来先是常量池的数量,也就是常量池数组的长度。后面紧跟着就是数组的内容,非常长,结构也不太一样。大家可以仔细看看附录中的详细图,太大了,在这就不截图了。

常量池中元素的子结构虽然有非常多的种类,但是都差不多的。首先是tag,tag的值表示这个元素是一个什么类型,也就是一个什么数据结构,然后JVM就可以根据这个结构来解析数据了。一般的XXX_index就是一个索引,length就是一个长度,bytes中是该元素存储的值。结构非常多,我们后面单独一篇文章介绍下这里。

4. access_flags 类访问标志

access_flags保存的信息是,该类的访问标志,比如是public还是private,是个接口还是个类,或者枚举,等等。

access_flags.jpg

此处 access_flags的值是0x0021,代表什么意思呢?我们需要看个表格:

名称

说明

ACC_PUBLIC

0x0001

public类型

ACC_FINAL

0x0010

final修饰符

ACC_SUPER

0x0020

使用invokespecial字节码指令,在JDK1.2之后添加

ACC_INTERFACE

0x0200

接口

还有好几种类型,我们就先看这些。我们的值是0x0021,好像没有这个值对应的类型。其实是,0x0020 加上 0x0001,也就是说0x0021表示ACC_PUBLIC和ACC_SUPER。也就是说,这是个public访问级别的类。

5. this_class 类名索引

this_class元素保存的是类的全限定类名,即包路径加类名。为什么还有个索引呢?因为这个地方保存的不是具体的全限定类名的字符串,是一个索引值。这个索引是常量池的索引,也就是说,其实真正的全限定类名字符串是在常量池存着呢。我们在附录的详细表中找一下这个值,找到第39行数据的最后,是this_class的位置。

this_class

它的值是0x000c,是十进制的12对吧?我们找常量池中的索引是12的值,在第4行最后。

index_12.jpg

很奇怪,这个地方比不是我们说的全限定类名字符串,是一个叫 name_index的索引。其实这个位置存放的数据,是有一个子结构的,它由tag和 name_index组成。tag的值是7,这个值7代表了一种数据结构,就是 CONSTANT_Class_info,这个类型保存的值是类或者接口的符号引用。又是一个引用,也就是索引。从表上我们可以知道,这个索引的值是49,OK,我们再从常量池找索引49的值。

index_49.jpg

在第26和27行上,我们终于找到了类的全限定名称。

6. super_class 父类名索引

super_class跟this_class是一样的,不过这里保存的是类的父类全限定名称而已。大家可以自己找一下看看。不过需要注意的是,如果没有明确指定某个类的父类,在Java中默认父类都是java.lang.Object,而java.lang.Object本身是没有父类的,所以如果是Object类,它的super_class值就是0。

7. interfaces 接口池

接着就是接口索引表或者说是接口池了,这里保存类实现的接口。

interfaces.jpg

因为我们的类没有实现接口,所以它的长度是0,后面的数组自然也就省略了。

8. fields 字段池

fields.jpg

接着是我们的字段信息。从 fields_count的值我们可以知道有4个字段,这跟我们在代码中的字段数量是一致的。然后紧接着就是字段数组了。字段数组中的元素是有数据结构的。如下:

field_info {

u2 access_flags; //变量的访问标识符

u2 name_index; //名称索引

u2 descriptor_index; //类型信息索引

u2 attributes_count; //自定义属性长度

attribute_info attributes[attributes_count]; //自定义属性池

}

name_index中存的就是字段名字的索引,老规矩大家自己找一下。access_flags是变量的访问标识符,descriptor_index保存的是变量的类型信息。最后剩下的属性,为什么有了类的属性池还会在这里有个字段下的属性池呢?这个字段下的属性池是留给JVM来自定义拓展的,各个JVM实现可能会不一样,JVM遇到识别不了的属性会自动跳过。

9. methods 方法池

大家可以看到,在附录详细大图中,方法池没有展开详细的结构,这是因为这里太复杂了,展开图不太好画,我太懒了,所以没画,哈哈哈哈。依然是上来就是一个长度,3个方法,后面跟着一个数组。有人要问了,不对啊,怎么是3个方法,我们只写了2个。那是因为还有个类的默认构造方法,忘了吧,哈哈。后面有机会我们详细分析方法池的结构。

10. attributes 属性池

属性池这个地方,跟常量池一样,保存了很多信息,不过我们这个类中的属性信息较少。属性也有很多的子结构,而且不同的JVM实现还可能会有自己的属性值,这里我们后面有机会单独说一下,大家现在知道即可。

附录

最后,附上一张非常详细的大图,我在图中画出了更详细的结构,包括常量池中子元素的结构,每个字节对应的值是什么等等,非常详细,也耗费了我很多时间,希望对大家有用。大家可以下载下来,经常看一看。

class.png

java jpg结构_Java Class 字节码文件结构分析----附带逐字节码分析图相关推荐

  1. java中反射机制通过字节码文件对象获取字段和函数的方法

    pclass = Class.forName("get_class_method.Person");//Field ageField = pclass.getField(" ...

  2. 【Java 虚拟机原理】线程栈 | 栈帧 | 局部变量表 | 反汇编字节码文件 | Java 虚拟机指令手册 | 程序计数器

    文章目录 一.线程栈 二.栈帧 三.栈帧 - 局部变量表 四.反汇编字节码文件 五.Java 虚拟机指令手册 六.程序计数器 一.线程栈 装载 HelloWorld.class 字节码文件到 Java ...

  3. 【Java 虚拟机原理】Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | info[] | 完整分析字节码文件中的常量池二进制数据 )

    文章目录 前言 一.常量池结构分析 1.常量池位置 2.常量池结构 3.常量池单个常量 4.常量池单个常量 tag 标签 二.常量池字节码文件分析 0.常量池附加信息 1.常量池 #1 常量分析 2. ...

  4. java byte 拓展_Java字节码文件的扩展名是( )。

    Java字节码文件的扩展名是( ). 答:.class There has not been a great response to the slate, _________?: does there ...

  5. class字节码文件中的常量池结构详解

    文章目录 前言 方法区 常量池基本结构 JVM 所定义的11种常量 常量池元素的复合结构 常量池的结束位置 常量池元素总数量 第一个常量池元素 父类常量 变量型常量池元素 自己的学习笔记,部分节选自& ...

  6. java文件解析器_jvm:java类文件结构(字节码文件的解析)

    1.java虚拟机简介 不是只有java编译器才能完成java程序到字节码的编译过程 (2)定义 java二进制字节码的运行环境 (3)好处 一次编写,到处运行的基础 自动内存管理,垃圾回收功能,大大 ...

  7. java中计算一个文件的总字节数_【JVM故事】一个Java字节码文件的诞生记

    作者:李新杰·转自微:信公众号"编程新说" 万字长文,完全虚构.(12000字) (一) 组里来了个实习生,李大胖面完之后,觉得水平一般,但还是留了下来,为什么呢?各自猜去吧. 李 ...

  8. 【开发环境】Android 命令行中执行 Java 程序 ( IntelliJ IDEA 中创建 Java / Kotlin 工程 | dx 打包 DEX 字节码文件 | dalvikvm 命令 )

    文章目录 前言 一.IntelliJ IDEA 中创建 Java / Kotlin 工程 二.准备 Java 和 Kotlin 代码 三.编译在 PC 上可执行的 Java / Kotlin JAR ...

  9. 【Java 虚拟机原理】Class 字节码二进制文件分析 一 ( 字节码文件附加信息 | 魔数 | 次版本号 | 主版本号 | 常量池个数 )

    文章目录 一.字节码文件 与 JVM 二.字节码文件示例 三.字节码文件二进制结构分析 1.魔数 2.次版本号 3.主版本号 4.常量池个数 一.字节码文件 与 JVM Java 源码编译成 Clas ...

最新文章

  1. Windows下svn客户端和服务器的安装使用
  2. 深入入门正则表达式(java) - 匹配原理 - 1 - 引擎分类与普适原则
  3. 历经8年双11流量洗礼,淘宝开放平台如何攻克技术难关?--转
  4. jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要)
  5. django model filter_django中探索如何提高查询数据效率
  6. 姚期智:人工智能存在三大技术瓶颈
  7. 信息学奥赛一本通 2005:【20CSPJ普及组】直播获奖 | 洛谷 P7072 [CSP-J2020] 直播获奖
  8. 提交form前先使用JS进行验证
  9. php 精度比较,PHP浮点数精度和比较
  10. linux 修改php配置,PHP部署时的几个配置修改说明
  11. Tp5.0完全开发手册学习(第六章 请求)之一 (request 和input)
  12. java视频教程 设计模式,Gof设计模式视频课程(Java实现)
  13. c 语言小游戏源程序,c/c++小游戏源代码
  14. 自用计算机一套送东西,购买笔记本电脑别忘记索要赠品
  15. 获取句子迷的经典语录[Python版本]
  16. 2011-11-27神马浮云的一天
  17. 博导谈寒门子弟上大学:要相信双一流大学没有“废物”!
  18. 使用for循环加if-else实现两个数最小公倍数和最大公约数的计算
  19. Kelvin Test
  20. Python中函数的常见操作(创建、调用、递归函数等等)【非常详细】

热门文章

  1. 再会迪杰斯特拉(Dijkstra)
  2. Leetcode--542. 01 矩阵(java)
  3. 可以操作excel吗_Excel快速填充,这四种方法你会吗?操作逆天告别加班
  4. excel表格打印每页都有表头_这么漂亮的Excel表格,用黑白打印机打印真是可惜了...
  5. linux高亮查找关键字
  6. pytorch每次迭代训练前都重新对数据集进行采样形成平衡数据集
  7. 编程关键词介绍...
  8. knn用于水果数据集分类
  9. FragmentPagerAdapter实现刷新
  10. DOS 命令、必会的 10个 DOS 命令