JAVA的class文件格式例解

JAVA程序从源代码到运行的一般过程为:
JAVA源文件编译之后形成class文件,也就是java字节码文件,然后java虚拟机解释执行
java字节码文件。我们来简单看看java字节码文件的格式。下图是java字节码文件的格式的定义。

u4代表占4个字节,cp_info表示cp_info结构需要占据的字节,后面会介绍cp_info结构。
这些count的最大长度意味着为0xFFFF=65535,如果超过,javac会造成“code too large”错误
假设有一个java文件的源代码如下

public class ClassFormatTest {
	public static String staticStr = "static";
	private int instanceInt = 1;
	private int[] intArray = { 1, 2, 3 };
	static{
		staticStr="changed in static block";
	}
	public int add(int c){
		int a = 10;
		int b = 23;
		int d = a + b;
		return d;		
	}
}
我们来看看编译成二进制文件后文件的内容,以16进制的形式打开,我们可以看到如下图所示:


ca fe ba be 为magic number,00 00 00 32 为此版本号和主版本号 0x32=50=jdk1.7
接下来是常量池的数量00 27=39,对应constant_pool_count,那么一共有39-1=38个常量项。
常量池是数组的形式。保存了类中用到的一些不变的"常量",如标识符public,private,
类名称,方法名,数据类型,字面值,变量名称等等。
接下来是第一个常量项,第一个字节07 是这个常量项的类型。
常量池中的16进制和类型的对应如下图所示:

根据常量池中的16进制和类型的对应关系图可以得知这是一个class的信息。
一个class的cp-info结构如下图所示:

00 02表示它的值为常量池数组的第二项。为const #2 = Asciz    ClassFormatTest;(参照后面javap反编译的结果)
字节码的项于项之间是没有空格的,接下来第二项01表示常量项type为utf8编码的字符串,
对应下图的utf8 string的cp-info结构,可以得知 00 0f是表示长度为15.
如上面二进制文件那副图中划线的内容,后面的15个字节的内容表示的字符串值为ClassFormatTest。

常量池一致持续到划椭圆的代表这个类的访问标志符 0x0021(public,jkd1.2之后)
16进制数和访问标识符对应关系如下图所示:

访问标识符(access flags)之后的00 01 00 03 00 00分别表示this class,supper class,
interfaces count,由于interfaces count是 0000所以interfaces没有字节表示。
接下来是fields的信息,如下图所示所示的0003这两个字节表示fields_count:


00 03表示fields 数量为3。
接下来是第一个字段的信息,参照下图中的字段结构对应关系,
接下来两个字节为access flag, 0x0009表示 public static,
再接下来两个字节为字段名,00 05指向常量池中的第5项,const #5 = Asciz    staticStr;(参照后面javap反编译的结果)
再接下来两个字节为字段类型,00 06指向常量池中的第6项,const #6 = Asciz    Ljava/lang/String;;
再接下来两个字节为字段属性个数,00 00表示没有attributes。
接下就是第二个字段的信息
0002为第二个field的access flag信息,表示 private。
00 07指向常量池中的第7项,const #7 = Asciz    instanceInt;
00 08指向常量池中的第8项,const #8 = Asciz    I;
00 00表示没有attributes。
其他字段类推...
字段结构对应关系如下图

methods_count字节位置的0003表示有3个方法,
接下来两个字节是第一个方法的访问标识符,0008表示为static
接下来两个字节为方法名,000b对应 const #11 = Asciz    <clinit>; (类构造器方法)
再接下来两个字节为方法描述,000c对应 const #12 = Asciz    ()V;
再接下来两个字节为属性个数,00 01表示有一个属性。
再接下来两个字节为属性值, 00 0d表示常连池的第13项const #13 = Asciz    Code;
下面是方法的结构关系图

接下来是方法代码的信息,根据下图code结构的对应关系分析:

我们来仔细分析一下add方法
划线的00 01 00 1f 00 20 00 01 00 0d中的
00 01是方法的访问标识符表示这是 public。
00 1f是方法的的名称add方法(const #31),
00 20表示descriptor为const #32 = Asciz    (I)I; 参数int,返回值为int。
00 01表示有一个属性,00 0d表示根据属性的index参照常量池中得#13项为代码。
接下来的4字节00 00 00 6c 表示属性的长度为 108。(这个长度包括code字节码,
exception,LineNumberTable和LocalVariableTable等)
在接下来的00 02 表示Stack=2,
00 05表示Locals=5,
接下来的4字节00 00 00 0e 表示code的长度为14。
接下来的14个字节为虚拟机的字节码指令,10 0a 3d 10 17 3e 1c 1d 60 36 04 15 04 ac
10 0a对应 bipush    10,3d 对应istore_2,10 17对应bipush    23,
3e对应istore_3,直到ac对应ireturn
两个字节合起来的话,后面的一个是操作数,有的指令是不需要操作数的则为一个字节。
接下来的0000表示exception_table_length为0.
再接下来的0002表示attributes_count为2.
再接下来的0014对应到常量池中的第20项,const #20 = Asciz    LineNumberTable;表示为LineNumberTable.

再接下来的00000012表示attributes_length 为18.
接下来为line_number_table_length,0004表示有4个行对应关系。
00 00 00 0b为一组对应,00 03 00 0c为一组对应,
00 06 00 0d为一组对应,00 0b 00 0e为一组对应。
接下来的0000 000b表示这个方法字节code第0个(也就是第一个)字节码指令与JAVA源码的11行对应。
最多两个字节表示行号,则java的方法的代码行数是有限制的。LineNumberTable的结构如下

另外一个attribute为LocalVariableTable,从0015开始,共有5个local变量。
根据如下的结构图可以分析:
这5个local变量为this,c,a,b和d。
以变量a为例字节码为 00 03 00 0b 00 22 00 08 00 02,
00 03 表示的信息为局部变量开始有效时字节码的偏移位置。
00 0b 表示的信息为作用范围覆盖的长度。上面两者结合判断作用范围。
00 22 表示的信息为名称,常量池中第34项对应的值,为const #34 = Asciz    a;
00 08 表示的信息为类型,常量池中第8项对应的值,const #8 = Asciz    I;
00 02 表示的信息为在栈中保存的位置。


方法之后是类的属性个数,attributes_count,在本例中为0001,表示有一个属性。
再接下来的0025表示attributes在常量池中的索引号,对应const #37 = Asciz    SourceFile;
表示为SourceFile属性,根据下面的SourceFile结构关系图可以得知
对应到源文件名为const #38 = Asciz    ClassFormatTest.java;

通过javap命令得到的内容如下:[javap -verbose -c ClassFormatTest > javap.txt]

Compiled from "ClassFormatTest.java"
public class ClassFormatTest extends java.lang.Object
  SourceFile: "ClassFormatTest.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = class	#2;	//  ClassFormatTest
const #2 = Asciz	ClassFormatTest;
const #3 = class	#4;	//  java/lang/Object
const #4 = Asciz	java/lang/Object;
const #5 = Asciz	staticStr;
const #6 = Asciz	Ljava/lang/String;;
const #7 = Asciz	instanceInt;
const #8 = Asciz	I;
const #9 = Asciz	intArray;
const #10 = Asciz	[I;
const #11 = Asciz	<clinit>;
const #12 = Asciz	()V;
const #13 = Asciz	Code;
const #14 = String	#15;	//  static
const #15 = Asciz	static;
const #16 = Field	#1.#17;	//  ClassFormatTest.staticStr:Ljava/lang/String;
const #17 = NameAndType	#5:#6;//  staticStr:Ljava/lang/String;
const #18 = String	#19;	//  changed in static block
const #19 = Asciz	changed in static block;
const #20 = Asciz	LineNumberTable;
const #21 = Asciz	LocalVariableTable;
const #22 = Asciz	<init>;
const #23 = Method	#3.#24;	//  java/lang/Object."<init>":()V
const #24 = NameAndType	#22:#12;//  "<init>":()V
const #25 = Field	#1.#26;	//  ClassFormatTest.instanceInt:I
const #26 = NameAndType	#7:#8;//  instanceInt:I
const #27 = Field	#1.#28;	//  ClassFormatTest.intArray:[I
const #28 = NameAndType	#9:#10;//  intArray:[I
const #29 = Asciz	this;
const #30 = Asciz	LClassFormatTest;;
const #31 = Asciz	add;
const #32 = Asciz	(I)I;
const #33 = Asciz	c;
const #34 = Asciz	a;
const #35 = Asciz	b;
const #36 = Asciz	d;
const #37 = Asciz	SourceFile;
const #38 = Asciz	ClassFormatTest.java;

{
public static java.lang.String staticStr;


static {};
  Code:
   Stack=1, Locals=0, Args_size=0
   0:	ldc	#14; //String static
   2:	putstatic	#16; //Field staticStr:Ljava/lang/String;
   5:	ldc	#18; //String changed in static block
   7:	putstatic	#16; //Field staticStr:Ljava/lang/String;
   10:	return
  LineNumberTable: 
   line 4: 0
   line 8: 5
   line 2: 10



public ClassFormatTest();
  Code:
   Stack=5, Locals=1, Args_size=1
   0:	aload_0
   1:	invokespecial	#23; //Method java/lang/Object."<init>":()V
   4:	aload_0
   5:	iconst_1
   6:	putfield	#25; //Field instanceInt:I
   9:	aload_0
   10:	iconst_3
   11:	newarray int
   13:	dup
   14:	iconst_0
   15:	iconst_1
   16:	iastore
   17:	dup
   18:	iconst_1
   19:	iconst_2
   20:	iastore
   21:	dup
   22:	iconst_2
   23:	iconst_3
   24:	iastore
   25:	putfield	#27; //Field intArray:[I
   28:	return
  LineNumberTable: 
   line 2: 0
   line 5: 4
   line 6: 9
   line 2: 28

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      29      0    this       LClassFormatTest;


public int add(int);
  Code:
   Stack=2, Locals=5, Args_size=2
   0:	bipush	10
   2:	istore_2
   3:	bipush	23
   5:	istore_3
   6:	iload_2
   7:	iload_3
   8:	iadd
   9:	istore	4
   11:	iload	4
   13:	ireturn
  LineNumberTable: 
   line 11: 0
   line 12: 3
   line 13: 6
   line 14: 11

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      14      0    this       LClassFormatTest;
   0      14      1    c       I
   3      11      2    a       I
   6      8      3    b       I
   11      3      4    d       I
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值