创客联盟网站建设,网站效果图制作,网站评论怎么做的,成都教育网站建设欢迎访问我的博客#xff0c;同步更新: 枫山别院 今天给大家分享下Java Class字节码文件的结构解析。(文末附有详细大图)代码首先我们创建一个Java类#xff0c;然后添加一些成员变量和方法#xff0c;如下:public class Test {String name hello;int size 10…欢迎访问我的博客同步更新: 枫山别院 今天给大家分享下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 0800 2A 09 00 0C 00 2B 09 00 0C 00 2C 05 00 00 0000 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 0000 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文件就是由以上元素一个紧挨一个组成的。总体的结构还是不复杂的开头的三个值magicminor_versionmajor_version和中间的access_flagsthis_classsuper_class他们是固定的位置固定的字节长度。其他的数量不一定的值都是一个长度然后后面就是紧跟着该长度的数组存储着该组值。数组结构通常都有下一级的子结构。我把字节文件按照上面的元素格式以不同的颜色划分了出来请大家看下结构图class-simple.png看图非常的一目了然读两遍书不如看一遍图啊。连续的相同颜色的格子是同一种元素的值。元素之间是紧紧的排列在一起大家可以看到结构非常的紧凑节省空间。看不清的同学可以下载下来放大一下看。分析1. magic 魔数我们知道有时候通过后缀识别文件是不准确的因为很容易就改掉后缀。所以很多文件格式在文件的开头写上几个固定的值为了识别方便该种格式。比如PDF文件的开头是“%PDF”这个固定的值就叫魔数其实就是个标识。class文件的魔数就是“CA FE BA BE”JVM读取开头的四个字节如果是这个值那么就认为这个文件是个class。magic.jpg2. version 版本号魔数之后是版本号大家可以看到我们有两部分的版本号。第一个版本号就是次版本号第二个是主版本号。比如52.052是主版本号0是次版本号。这个版本是为了让JVM识别编译这个clas文件的Java版本比如Java SE8对应的版本号是52.0Java SE7 是51.0Java SE6 是50.0。如果一个最高只支持Java SE7版本的JVM读取到一个52.0那么它可能是执行不了这个class的以为它是Java SE8编译出来的可能用了JavaSE8的新特性。version.jpg3. constant_pool 常量池常量池占用了class中非常多的空间存放着非常多的信息包括数字字符串类和接口的名字字段和方法的名字等等。上来先是常量池的数量也就是常量池数组的长度。后面紧跟着就是数组的内容非常长结构也不太一样。大家可以仔细看看附录中的详细图太大了在这就不截图了。常量池中元素的子结构虽然有非常多的种类但是都差不多的。首先是tagtag的值表示这个元素是一个什么类型也就是一个什么数据结构然后JVM就可以根据这个结构来解析数据了。一般的XXX_index就是一个索引length就是一个长度bytes中是该元素存储的值。结构非常多我们后面单独一篇文章介绍下这里。4. access_flags 类访问标志access_flags保存的信息是该类的访问标志比如是public还是private是个接口还是个类或者枚举等等。access_flags.jpg此处 access_flags的值是0x0021代表什么意思呢我们需要看个表格名称值说明ACC_PUBLIC0x0001public类型ACC_FINAL0x0010final修饰符ACC_SUPER0x0020使用invokespecial字节码指令在JDK1.2之后添加ACC_INTERFACE0x0200接口还有好几种类型我们就先看这些。我们的值是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这个类型保存的值是类或者接口的符号引用。又是一个引用也就是索引。从表上我们可以知道这个索引的值是49OK我们再从常量池找索引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