java class文件结构
看了一周深入理解java虚拟机了,今天看到第六章,最近一直处在接受新知识的快感之中,不由得感慨基础知识的重要性,学起来相当过瘾!类文件结构这部分实践性较强,对于分析java代码有很重要的帮组,于是有做点笔记的必要了,在参考书的基础上,将一段代码的字节码完全解析了一遍。
class文件的结构如下
class文件的结构如下
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_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]; }编译下面这段代码生成class文件
package org.fenixsoft.clazz; public class Test { private int m; public int inc(){ return m+1; } }
对应class文件内容如下
cafe babe 0000 0033 0016 0700 0201 0018 6f72 672f 6665 6e69 7873 6f66 742f 636c 617a 7a2f 5465 7374 0700 0401 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 016d 0100 0149 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0a00 0300 0b0c 0007 0008 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 124c 6f63 616c 5661 7269 6162 6c65 5461 626c 6501 0004 7468 6973 0100 1a4c 6f72 672f 6665 6e69 7873 6f66 742f 636c 617a 7a2f 5465 7374 3b01 0003 696e 6301 0003 2829 4909 0001 0013 0c00 0500 0601 000a 536f 7572 6365 4669 6c65 0100 0954 6573 742e 6a61 7661 0021 0001 0003 0000 0001 0002 0005 0006 0000 0002 0001 0007 0008 0001 0009 0000 002f 0001 0001 0000 0005 2ab7 000a b100 0000 0200 0c00 0000 0600 0100 0000 0300 0d00 0000 0c00 0100 0000 0500 0e00 0f00 0000 0100 1000 1100 0100 0900 0000 3100 0200 0100 0000 072a b400 1204 60ac 0000 0002 000c 0000 0006 0001 0000 0006 000d 0000 000c 0001 0000 0007 000e 000f 0000 0001 0014 0000 0002 0015
解析:
/*魔数*/ cafe babe // 魔数 /*次版本*/ 0000 // 次版本 /*主版本*/ 0033 // 主版本 /*常量池个数*/ 0016 // 常量个数,21(从1开始) /*cp_info类型的数组*/ //21个常量 0700 0201 0018 6f72 672f 6665 6e69 7873 6f66 742f 636c 617a 7a2f 5465 7374 0700 0401 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 016d 0100 0149 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0a00 0300 0b0c 0007 0008 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 124c 6f63 616c 5661 7269 6162 6c65 5461 626c 6501 0004 7468 6973 0100 1a4c 6f72 672f 6665 6e69 7873 6f66 742f 636c 617a 7a2f 5465 7374 3b01 0003 696e 6301 0003 2829 4909 0001 0013 0c00 0500 0601 000a 536f 7572 6365 4669 6c65 0100 0954 6573 742e 6a61 7661 /*访问标志*/ 0021 // 访问标志(使用标志位) /*类索引*/ 0001 // 类索引 /*父类索引*/ 0003 // 父类索引 /*接口个数*/ 0000 // 接口索引:u2类型的接口计数器 + 各个接口 /*u2类型的接口数组*/ /*字段表数量*/ 0001 // 字段表数量 /*field_info类型数组*/ 0002 // 字段访问标志 0005 // 简单名称 0006 //描述符 0000 // 字段的属性表数量 --- // 字段的属性表集合(0个) /*方法表数量*/ 0002 // 方法表数量 /*method_info数组*/ //第一个方法 0001 // 方法访问标志 0007 // 简单名称 0008 // 描述符 0001 // 属性表数量 // 方法的属性表(对应的常量是Code,方法表中使用,代表代码编译成的字节码指令) 0009 // Code属性表 u2类型,在常量池中 0000 002f // u4类型,指明属性值所占位数 0001 //max_stack,操作数栈最大深度,根据它分配栈帧 0001 //max_locals,局部变量表所需的存储空间,单位是Slot,不超过32位的数据类型,占一个Slot,64位的占2个Slot 0000 0005 // code length, // code,共5个 2a // aload_0(将第0个Slot中为refrence类型的本地变量推送至操作数栈顶,其实就是this) b7 // invokespecial(调用栈顶对象的某个方法:实例构造方法、private方法或者它的父类方法) 000a // invokespecial的参数,说明具体调用哪个方法,指向常量池中CONSTANT_Methodref_info类型常量 b1 // 返回return,返回值为void 0000 // 异常表长度 //异常信息,长度为0 ---- 0002 // code的属性表数量 // 方法的属性表,在Code属性表中 000c // 表示LineNumberTable,java源码与字节码的偏移量之间的关系 0000 0006 // 属性表长度 0001 // 行号表的长度 // 行号信息 0000 // start_pc 0003 // line_number // 方法的属性表,在Code属性表中 000d // 表示LocalVariableTable,描述栈帧中局部变量表中的变量与java源码中定义的变量之间的关系 0000 000c // 该属性表长度 0001 // 局部变量表长度 // 局部变量信息,长度为1,由5个u2类型构成 0000 // start_pc,该局部变量的生命周期开始的字节码偏移量 0005 // length,其作用范围覆盖的长度 000e // name_index,指向常量池中的索引,局部变量的名称 000f // descriptor_index,同上,局部变量的描述符 0000 // index,该局部变量在栈帧局部变量表中的Slot位置 // 第二个方法 0001 // 访问标志 0010 // 简单名称,inc 0011 // 描述符()I 0001 // 属性表数量 0009 // // Code属性表 0000 0031 // u4类型,指明属性值所占位数 0002 // max_stack 0001 // max_locals 0000 0007 // code_length 2a // aload_0(将第0个Slot中为refrence类型的本地变量推送至操作数栈顶,其实就是this) b4 // getfield,获取指定类的实例,并将其压入栈帧 0012 // 参数,Test类的m成员 04 // iconst_1,将int型1推送至栈顶 60 // iadd,将栈顶两个int型数值相加并将结果压入栈顶 ac // ireturn,从当前方法返回int 0000 // 异常表长度 ---- //异常信息,长度为0 0002 // 属性表数量 // LineNumberTable 000c 0000 0006 0001 0000 0006 // LocalVariableTable 000d 0000 000c 0001 0000 0007 000e 000f 0000 /*类的属性表个数*/ 0001 // 属性表数量 /*类的attribute_info数组*/ 0014 // SourceFile,记录原文件名称 0000 0002 // 属性表长度 0015 // 源文件,Test.java
属性表可以包含在Class文件、字段表、方法表中。
短短几行代码,我们看起来如此费劲,使用Oracle公司给我们准备的class文件字节码的工具javap。javap -v Test.java命令查看的字节码的内容如下,两者内容是一模一样的
Last modified 2015-12-11; size 378 bytes MD5 checksum 0e2626e6fdb11bccf73564affa0b63e2 Compiled from "Test.java" public class org.fenixsoft.clazz.Test SourceFile: "Test.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Class #2 // org/fenixsoft/clazz/Test #2 = Utf8 org/fenixsoft/clazz/Test #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 m #6 = Utf8 I #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Methodref #3.#11 // java/lang/Object."<init>":()V #11 = NameAndType #7:#8 // "<init>":()V #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Lorg/fenixsoft/clazz/Test; #16 = Utf8 inc #17 = Utf8 ()I #18 = Fieldref #1.#19 // org/fenixsoft/clazz/Test.m:I #19 = NameAndType #5:#6 // m:I #20 = Utf8 SourceFile #21 = Utf8 Test.java { public org.fenixsoft.clazz.Test(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/fenixsoft/clazz/Test; public int inc(); flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: getfield #18 // Field m:I 4: iconst_1 5: iadd 6: ireturn LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lorg/fenixsoft/clazz/Test; }