java class文件结构

看了一周深入理解java虚拟机了,今天看到第六章,最近一直处在接受新知识的快感之中,不由得感慨基础知识的重要性,学起来相当过瘾!类文件结构这部分实践性较强,对于分析java代码有很重要的帮组,于是有做点笔记的必要了,在参考书的基础上,将一段代码的字节码完全解析了一遍。
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;
}

原文地址:https://www.cnblogs.com/qhyuan1992/p/5385273.html