分析一个Java Class文件

Java源码文件TestClass.java:

package jvm.chapter6;
//P166
public class TestClass {
	private int m;
	public int inc(){
		return m+1;
	}
}

展示这个Class文件的16进制内容:

从头开始分析=>

ca fe ba be :magic number;

00 00 00 34 : 版本号是1.8.0;

00 16: 说明常量池有21个常量,1-21, index留做他用;接下来就是分别这21个常量的描述:

07/00 02 :CONSTANT_Class_info 常量,类名索引是该常量池的第2项;

01/00 16/6a 76 6d 2f 63 68 61 70  74 65 72 36 2f 54 65 73 74 43 6c 61 73 73 :这是该常量池的第2项,CONSTANT_Utf8_info常量,长度是22(0x0016),正是字符串常量“jvm/chapter6/TestClass”;

07/00  04:CONSTANT_Class_info 常量,类名索引是该常量池的第4项;

01/00 10/6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74:这是该常量池的第4项,CONSTANT_Utf8_info常量,长度是16(0x0010),正是字符串常量“java/lang/Object”;

01/00 01/6d :CONSTANT_Utf8_info常量,是字符串常量"m";

01/00 01/49 :CONSTANT_Utf8_info常量,是字符串常量"m";

01/00 06/3c  69 6e 69 74 3e :CONSTANT_Utf8_info常量,是字符串常量"<init>";

01/00 03/28 29 56 :CONSTANT_Utf8_info常量,是字符串常量"()V";

01/00 04/43 6f  64 65 :CONSTANT_Utf8_info常量,是字符串常量"Code";

0a/00 03/00 0b: tag=10,表示CONSTANT_Methodref_info常量,声明该方法的类名称位于常量池的第3项,即java/lang/Object,方法名和类型NameAndType="<init>":()V;

0c/00 07/00 08: 接下来tag=12就是上面刚说道的CONSTANT_NameAndType_info类型,接下来的两个索引分别指向方法名和描述符即"<init>"和“()V”,

01/00 0f/4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"LineNumberTable";

01/00 12/4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61  62 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"LocalVariableTable";

01/00 04/74 68  69 73 :CONSTANT_Utf8_info常量,是字符串常量"this";

01/00 18/4c 6a 76 6d 2f 63 68 61 70 74 65 72 36 2f 54 65 73 74 43 6c 61 73 73 3b:CONSTANT_Utf8_info常量,是字符串常量"Ljvm/chapter6/TestClass;";

01/00 03 69 6e 63 :CONSTANT_Utf8_info常量,是字符串常量"inc";

01/00 03 28 29 49 :CONSTANT_Utf8_info常量,是字符串常量"()I";

09/00 01/00 13:代表Fieldref常量,然后接下来两个u2分别表示声明该字段的类和NameAndType的索引;

0c/00 05/00 06:就是上面刚使用的NameAndType常量;

01/00 0a/53 6f 75 72 63 65 46 69 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"SourceFile";

01 00 0e 54 65 73 74 43  6c 61 73 73 2e 6a 61 76 61::CONSTANT_Utf8_info常量,是字符串常量"TestClass.java";

上面就分析完了常量池中的字段,

00 21 : access_flags= ACC_PUBLIC | ACC_SUPER = 0x0021;

00 01 00 03 00  00:类索引 父类索引 和接口索引,常量池第3项表示的是Object,说明了默认的父类就是Object,没有实现接口,所以interfaces_count=0;

00 01/00 02/00 05/00 06/00 00: 接下来的是字段表集合(fields_info),fields_count=1,access_flags=0x0002=ACC_PRIVATE, 代表字段名称的name_index=5(常量池的第5项是CONSTANT_Utf8_info类型的字符串m),字段描述符descriptor_index=0x0006(常量池的第5项是CONSTANT_Utf8_info类型的字符串I),最后atributes_count=0,说明不包含额外的信息。总上这些信息正是为了说明定义的字段“private
int m”;

00 02/00 01/00  07/00 08/00 01/00 09/00 00 00 2f/00 01/00 01/00  00 00 05/2a b7 00 0a b1:接下来的是方法集合(methods_info),有两个方法,第一个方法的访问标志是0x0001=ACC_PUBLIC,名称索引是0x0007,指向常量池中的<init>,描述符索引值是0x0008,指向常量池中的"()V",接下来属性表计数器attributes_count=1,属性名称索引是0x0009对应的是“Code”,接下来就是Code属性表的结构:属性值的长度attribute_length=0x2f;操作数栈的最大深度和本地变量表的容量都是0x0001;字节码指令长度是5,翻译“2a
b7 00 0a b1”的过程是:

1)读入2a,对应的是aload_0 ,将第一个引用类型的本地变量推送到操作数栈顶;

2)读入b7,对应的是invokespecial,作用是调用以栈顶的reference类型所指向的对象的构造器方法 实例初始化方法 私有方法,接下来有一个u2类型的参数说明具体调用哪个方法,它指向常量池中一个CONSTANT_Methodref_info的常量,即那个方法的符号引用;

3)00 0a正是上面invokespecial的参数,指向常量池中的一个Methodref常量,对应的应该是Object.init()方法;

4)b1,对应的指令是return,并且返回类型是oid,当前方法对应的指令结束。

继续上面的分析,

00 00/00 02/00 0c/00 00 00 06/00 01/00 00/00 03|00 0d/00 00 00 0c/00 01|00 00/00 05/00 0e/00 0f/00 00:exception_table_length=0,该方法没有抛出异常,然后attributes_count=2,该Code属性有俩属性:0x000c指向常量池中的“LineNumberTable”,attribute_length=6,line_number_table_length=1,就是记录了一对java源码行号和字节码行号的对应关系(3:0);0x000d指向常量池中的“LocalVariableTable”,attribute_length=12,local_variable_table_length=1(代表了一个栈帧和程序中的局部变量关联关系),start_pc=0x0000,length=0x0005表征该局部变量在字节码中的作用范围(就是这个方法内),name_index=0x000e,
descriptor_index=0x000f表示该局部变量的名称及其描述符,即为“this”, "Ljvm/chapter6/TestClass";通过javap的输出都可以一目了然。index=0x0000代表该局部变量在栈帧局部变量表中Slot的位置。

接下来看第二个方法,

00 01/00 10/00 11/00 01/00 09/00 00 00 31/00 02/00 01/00 00 00 07/2a b4 00 12 04 60 ac/:方法的访问标志是0x0001=ACC_PUBLIC,名称索引是16,指向常量池中的“inc”,描述符索引值是0x0011,指向常量池中的"()I",接下来属性表计数器attributes_count=1,属性名称索引是0x0009对应的是“Code”,接下来就是Code属性表的结构:属性值的长度attribute_length=0x31;操作数栈的最大深度是2,本地变量表的容量是1;字节码指令长度是7,翻译“2a
b4 00 12 04 60 ac”的过程是:

1)读入2a,对应的是aload_0 ,将第一个引用类型的本地变量推送到操作数栈顶;

2)读入b4,对应的指令是getfield,获得实例域,并压入栈顶,他需要参数用来说明把哪个变量入栈,即下面;

3)读入00 12,用来指明getfield 哪个字段,指向常量池中的一个Fieldref常量,“jvm/chapter6/TestClass.m:I”;

4)读入04,对应的指令是iconst_1,将int类型的1推至栈顶;

5)读入60,对应的指令是iadd,将栈顶两个int类型的数值相加并把结果压入栈顶;

6)读入ac,对应的指令是ireturn,从当前方法返回int,结束。

后面的俩属性就和分析第一个方法类似。

至此,就分析完了一个简单的Class文件的结构,清晰了很多。

附:

Javap TestClass.class的结果:$ javap -verbose TestClass.class

Classfile /home/vonzhou/GitHub/JavaProject/learning-java/bin/jvm/chapter6/TestClass.class

Last modified Apr 29, 2015; size 379 bytes

MD5 checksum 70a67e773b621619d03f9d1b5ac91af6

Compiled from "TestClass.java"

public class jvm.chapter6.TestClass

SourceFile: "TestClass.java"

minor version: 0

major version: 52

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Class              #2;            //  jvm/chapter6/TestClass

#2 = Utf8               jvm/chapter6/TestClass;

#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               Ljvm/chapter6/TestClass;;

#16 = Utf8               inc;

#17 = Utf8               ()I;

#18 = Fieldref           #1.#19;        //  jvm/chapter6/TestClass.m:I

#19 = NameAndType        #5:#6;         //  m:I

#20 = Utf8               SourceFile;

#21 = Utf8               TestClass.java;

{

public jvm.chapter6.TestClass();

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   Ljvm/chapter6/TestClass;

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   Ljvm/chapter6/TestClass;

}

时间: 2024-12-06 12:14:53

分析一个Java Class文件的相关文章

HTTP POST请求报文格式分析与Java实现文件上传

时间 2014-12-11 12:41:43  CSDN博客 原文  http://blog.csdn.net/bboyfeiyu/article/details/41863951 主题 HTTPHttpComponents 在开发中,我们使用的比较多的HTTP请求方式基本上就是GET.POST.其中GET用于从服务器获取数据,POST主要用于向服务器提交一些表单数据,例如文件上传等.而我们在使用HTTP请求时中遇到的比较麻烦的事情就是构造文件上传的HTTP报文格式,这个格式虽说也比较简单,但也

Java基础-一个java文件多个类的问题

一个.java文件当然可以包括多个类.但这些类有一个特殊的类与其它的不同,,这个类是带public 属性的类.一个.java类文件中仅有一个public属性的类.而且这个类与文件名相同.

关于:Java 类文件的内部情况课程详析

没有比发现一个错误,却没有源代码就不能修改更令人沮丧的了.正是这个原因导致了 Java 反编译器的出现,它可以把编译后的字节码完全转回成源代码.尽管代码反编译器不只是针对 Java 语言,但它从来没有象在 Java 开发人员中那样被公开地或广泛地使用. 与反编译针锋相对的是模糊处理.假设反编译人员能很容易从编译后的代码中设法得到源代码,那么要保护您的代码和有价值的技术秘密就不是那么简单了.随着 Java 反编译器的普遍使用, Java 模糊处理器也同样被普及,它的作用就好像放一块烟幕在您的代码前

Java类文件最大限制

今天在往一个jsp文件里添加代码时,项目跑起来访问这个jsp时报错.. The code of method _jspService(HttpServletRequest, HttpServletResponse) is exceeding the 65535 bytes limit _jspService方法超过65535 bytes最大限制. 额,这是什么鬼,看不懂啊.于是问问度娘,发现Java对类文件有个限制,不能超过65K字节.因为我改动的这个jsp已经很大了,而jsp文件在weblog

Java class文件分析工具 -- Classpy

Classpy Classpy是一个图形化的class文件分析工具,功能和javap类似,界面主要参考了Java Class Viewer: 为什么要重新创造轮子? 写这个工具花了将近一周的时间,那么为什么要浪费时间重新发明一个轮子呢?主要是因为下面几点原因: 通过自己写一个class解析器,可以彻底理解class文件格式和字节码 尝鲜Java8和JavaFX 8 Java Class Viewer比较老,不支持新的class文件格式 可以结合javap和Java Class Viewer的优点

关于java中文件删除失败的原因分析

最近在做一个文档管理系统,结果在删除文件的时候,一直提示我文件删除失败,当然啦,是我在jsp里面写的一个alert("文件删除失败!"),然后我就纳闷儿了,为什么删不掉呢?后来打开windows,找到相应的文件,用管理员权限去删除也删不掉!然后就给我报错,java TM...正在使用这个文件,我顿时就凌乱了,因为我使用的是MyEcplise,所以我又回去检查代码,后来终于找到元凶了,是一个警告导致的错误!警告啊!下面我贴上代码: /** * 获取单个文件的大小 * @param fil

java 中 “文件” 和 “流” 的简单分析

java 中 FIle 和 流的简单分析 File类 简单File 常用方法 创建一个File 对象,检验文件是否存在,若不存在就创建,然后对File的类的这部分操作进行演示,如文件的名称.大小等 //创建一个File 对象,检验文件是否存在,若不存在就创建然后对File package wfu; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.Scanner; pu

一个Java写的批量重命名文件小程序

今天学了一下java的File操作,然后乘着兴趣,写了一个可以批量处理文件命名的小程序,小程序还有一些不完美的地方,但胜在有趣.比如可以快捷更改你不想让别人看到的文件之类的...限个人使用,造成数据丢失后果自负哟. import java.io.File; import java.sql.Date; import java.util.Scanner; public class RenameTool { boolean useDefaultName = false; boolean useDefa

一个java文件编译之后会产生多个class文件

如图所示:如果编译后一个java文件中类有内部类的话,就会编译产生多个类