- 浏览: 273406 次
- 性别:
- 来自: 石家庄
最新评论
-
路小尘:
mark
详解 Java 语言中 float 类型的运算 -
ezerg:
兄弟,你那样写的效果和没写是一样的,因为你没有转换编码。例如从 ...
JDBC 中 DatabaseMetaData 接口的使用 -
yl419440513:
我也用的是这个,可是却被一个问题难住了,就是getString ...
JDBC 中 DatabaseMetaData 接口的使用 -
laijinyan:
我初学请教下不是说 MaxClients 要小于 Server ...
Apache 的 httpd 进程占用大量内存原因及其解决方案 -
ybb896:
,不错
安装和使用 percona 推出的 Xtrabackup 备份 MySQL
了解 .class 的文件结构,有助于加深对 Java 语言的理解和程序的优化。特别是深入了解之后,可以从原理上理解 Java 语言的很多底层的技术。
针对上一次利用 ASM 修改字节码的内容,以下的内容可能更难理解一些,也需要一些虚拟机字节码方面的知识。
Java 编译后的 .class 文件主要分为以下五个部分(个人理解):
1、魔数和版本号信息
2、常量池信息,包括池中常量的数量和每个常量的描述
3、类信息,包括本身类、父类和接口描述
4、类中声明的属性和方法信息,包括属性和方法的数量和描述,方法中还包括实际执行的虚拟机字节码
5、类本身属性的信息,例如 SourceFile 属性显示类的源文件名称
以下面的 Java 源文件为例,按照上面五个部分说明一下编译后的 .class 文件的格式。
注意:以下使用的数字如无特别说明均为 十六进制。
第一部分占用 8 个字节:
前面 4 个字节是魔数,所有 .class 文件都是一样的,为了虚拟机更方便的识别是否为类文件
后面 4 个字节分别是主版本号和次版本号,每个版本 JDK 的编译出的类会有所不同
第二部分占用的字节数取决的常量池的数量和常量的类型:
前面两个字节 0029 表示常量池的大小 40 个,常量池的内容会被后面所有部分使用到。
第1个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=0002,查看 2 个常量
第2个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16 个字节。内容为 test/PrintString
第3个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=0004,查看 4 号常量
第4个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16 个字节。内容为 java/lang/Object
第5个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0003 说明占用后面的 3 个字节。内容为 str
第6个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18 个字节。内容为 Ljava/lang/String;
第7个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0006 说明占用后面的 6 个字节。内容为 <init>
第8个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0003 说明占用后面的 4 个字节。内容为 ()V
第9个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0004 说明占用后面的 4 个字节。内容为 Code
第10个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0003,name_and_type_index=000B。内容为 java/lang/Object 的 init 方法
第11个常量 0C 是一个 constant_nameAndType 类型,后面的 4 个字节属于它,name_index=0007,descriptor_index=0008,内容为<init>()V
第12个常量 08 是一个 constant_String 类型,后面的 2 个字节属于它,000D 查看13号常量池
第13个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 00 说明占用后面的 0 个字节。内容为空
第14个常量 09 是一个 constant_Fieldref 类型,后面的 4 个字节表示它占用的字节数,class_index=0001 , name_and_type_index =000F。内容为 test/PrintString 的 str
第15个常量 0C 是一个 constant_nameAndType 类型,后面的 4 个字节属于它,name_index=0005,descriptor_index=0006,。内容为 Ljava/lang/String; str
第16个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,000F 说明占用后面的 15 个字节。内容为 LineNumberTable
第17个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18个字节。内容为 LocalVariableTable
第18个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0004 说明占用后面的 4个字节。内容为 this
第19个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18个字节。内容为 Ltest/PrintString;
第20个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0009 说明占用后面的 9个字节。内容为 setString;)
第21个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0015 说明占用后面的 21个字节。内容为 (Ljava/lang/String;)V
第22个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0001 说明占用后面的 1个字节。内容为 s
第23个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0009 说明占用后面的 9个字节。内容为 getString
第24个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0014 说明占用后面的 20个字节。内容为 ()Ljava/lang/String;
第25个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0005 说明占用后面的 20个字节。内容为 print
第26个常量 09 是一个 constant_Fieldref 类型,根据它的定义后面四个字节属于它,class_index=001B 查看27号常量, name_and_type_index = 001D 查看29号常量
第27个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=001C,查看28号常量
第28个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16个字节。内容为 java/lang/System
第29个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=001E 查看30号常量,descriptor_index=001F 查看31号常量
第30个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 03 说明占用后面的 3个字节。内容为 out
第31个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 15 说明占用后面的 21个字节。内容为Ljava/io/PrintStream;
第32个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0001,name_and_type_index=0021
第33个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=0017 查看23号常量,descriptor_index=0018 查看24号常量
第34个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0023 查看35号常量,name_and_type_index=0025 查看37号常量
第35个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=00 24,查看36号常量
第36个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 13 说明占用后面的 19个字节。内容为 java/io/PrintStream
第37个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=0026 查看38号常量,descriptor_index=0015 查看21号常量
第38个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0007 说明占用后面的 7个字节。内容为 println
第39个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,000A 说明占用后面的 10个字节。内容为 SourceFile
第40个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 10 说明占用后面的 16个字节。内容为 PrintString.java
第三部分占用 8 个字节:
前面 2 个字节 0021 表示类本身的修饰符
中间 2 个字节 0001 表示类本身的常量表索引,内容为 test/PrintString
下面 2 个字节 0003 表示父类的常量表索引,内容为 java/lang/Object
最后 2 个字节 0000 表示类实现的接口数量,此类未实现接口
第四部分是类文件是比较复杂的部分,它分为两个部分:属性区和方法区。
先看属性区:
前面 2 个字节 0001 表示类声明了一个属性
下面 2 个字节 0002 表示后面 2 个字节是属性的描述
下面 2 个字节 0005 表示属性名称的常量池索引,5号常量的信息 str
下面 2 个字节 0006 表示属性描述的常量池索引,6号常量的信息 Ljava/lang/String;
最后 2 个字节 0000 表示属性本身的属性的数量
再看方法区:
前面 2 个字节 0004 表示类声明四个方法(注意:包含一个隐含的构造方法 init)
下面 2 个字节 0001 表示是一个ACC_PUBLIC 的方法
下面 2 个字节 0007 表示 name_index,表示常量池中第7个常量为<init>,
下面 2 个字节 0008 表示desciptor_index,表示常量池第8个常量为 ()V
下面 2 个字节 0001 表示attribute_count,表示有1个attribute
下面 2 个字节 0009 表示code_attribute,查看第9号常量池为Code
下面4 个字节 0000003D 表示后面的 61 个字节属于这个属性
下面 2 个字节 0002 表示 max_stack 该方法执行的时候操作数栈最大的长度,这里表示操作数栈的长度为1
下面 2 个字节 0001 表示 max_locals 该方法局部变量所需要的空间的长度
下面 4 个字节 00 00 00 0B 表示code_length 即后面的 11 个字节为虚拟机字节码内容
代码的 11 个字节:2A B7 00 0A 2A 12 0C B5 00 0E B1
2A :aload_0 表示将第一个引用类型本地变量推送至栈顶
B7 :invokespecial 调用超类方法,后面的 000A,查看10号常量池 表示调用超类的init方法
2A :aload_0 表示将第一个引用类型本地变量推送至栈顶
12 :ldc 表示将一个常量池压入操作栈,后面的 0C 便是这个操作数,查看第13号常量池,为空
B5 :putfield 将操作栈的值赋给类变量,后面2个字节 000E 代表类变量的常量池索引,为 Ljava/lang/String; str
B1 : return ;返回void
这里我们可以知道类变量的初始化位置。
代码的 11 个字节以后
下面 2 个字节 0000 表示exception_table_length=0;也就是说没有异常处理
下面 2 个字节 0002 表示attributes_count=2,接下来有两个attribute_info 的结构
下面 2 个字节 0010 表示 attribute_name_index,查看10号常量池,为 LineNumberTable
下面 4 个字节 00 00 00 0E 表示attribute_length=14
下面 14 个字节是 LineNumberTable 的属性
下面 2 个字节 0011 表示 attribute_name_index,查看10号常量池,为 LocalVariableTable
下面 4 个字节 00 00 00 0C 表示attribute_length=12
下面 12 个字节是 LocalVariableTable 的属性
以上只是第一个方法的说明,后面的方法不再详细说明。
第五部分通常不是很重要,大概了解一下:
前面 2 个字节 0001 表示接下去有一个 attributes_info 结构,该结构占用 8 个字节
下面 2 个字节 0027 表示属性的名称的常量池索引,39 号常量的信息(属性的名称)为 SourceFile
下面 4 个字节 00000002 表示属性的长度,因为格式固定,所长度为 2
最后 2 个字节 0028 表示 sourcefile_index 的常量池索引, 40 号常量的信息(源文件的名称)为 PrintString.java
以上只是对一个结构简单的类作了一个简要说明,实际情况会复杂很多。详细内容大家可以参考《深入 Java 虚拟机(第二版)》
针对上一次利用 ASM 修改字节码的内容,以下的内容可能更难理解一些,也需要一些虚拟机字节码方面的知识。
Java 编译后的 .class 文件主要分为以下五个部分(个人理解):
1、魔数和版本号信息
2、常量池信息,包括池中常量的数量和每个常量的描述
3、类信息,包括本身类、父类和接口描述
4、类中声明的属性和方法信息,包括属性和方法的数量和描述,方法中还包括实际执行的虚拟机字节码
5、类本身属性的信息,例如 SourceFile 属性显示类的源文件名称
以下面的 Java 源文件为例,按照上面五个部分说明一下编译后的 .class 文件的格式。
public class PrintString { private String str = ""; public void setString(String s) { this.str = s; } public String getString() { return this.str; } public void print(){ System.out.println(getString()); } }
注意:以下使用的数字如无特别说明均为 十六进制。
第一部分占用 8 个字节:
00000000h: CA FE BA BE 00 00 00 32 ; 漱壕...2
前面 4 个字节是魔数,所有 .class 文件都是一样的,为了虚拟机更方便的识别是否为类文件
后面 4 个字节分别是主版本号和次版本号,每个版本 JDK 的编译出的类会有所不同
第二部分占用的字节数取决的常量池的数量和常量的类型:
00000008h: 00 29 07 00 02 01 00 10 74 65 73 74 2F 50 72 69 ; .)......test/Pri 00000018h: 6E 74 53 74 72 69 6E 67 07 00 04 01 00 10 6A 61 ; ntString......ja 00000028h: 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 ; va/lang/Object.. 00000038h: 03 73 74 72 01 00 12 4C 6A 61 76 61 2F 6C 61 6E ; .str...Ljava/lan 00000048h: 67 2F 53 74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 ; g/String;...<ini 00000058h: 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 0A ; t>...()V...Code. 00000068h: 00 03 00 0B 0C 00 07 00 08 08 00 0D 01 00 00 09 ; ................ 00000078h: 00 01 00 0F 0C 00 05 00 06 01 00 0F 4C 69 6E 65 ; ............Line 00000088h: 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F ; NumberTable...Lo 00000098h: 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 ; calVariableTable 000000a8h: 01 00 04 74 68 69 73 01 00 12 4C 74 65 73 74 2F ; ...this...Ltest/ 000000b8h: 50 72 69 6E 74 53 74 72 69 6E 67 3B 01 00 09 73 ; PrintString;...s 000000c8h: 65 74 53 74 72 69 6E 67 01 00 15 28 4C 6A 61 76 ; etString...(Ljav 000000d8h: 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 ; a/lang/String;)V 000000e8h: 01 00 01 73 01 00 09 67 65 74 53 74 72 69 6E 67 ; ...s...getString 000000f8h: 01 00 14 28 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F ; ...()Ljava/lang/ 00000108h: 53 74 72 69 6E 67 3B 01 00 05 70 72 69 6E 74 09 ; String;...print. 00000118h: 00 1B 00 1D 07 00 1C 01 00 10 6A 61 76 61 2F 6C ; ..........java/l 00000128h: 61 6E 67 2F 53 79 73 74 65 6D 0C 00 1E 00 1F 01 ; ang/System...... 00000138h: 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F 69 6F ; ..out...Ljava/io 00000148h: 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 0A 00 01 ; /PrintStream;... 00000158h: 00 21 0C 00 17 00 18 0A 00 23 00 25 07 00 24 01 ; .!.......#.%..$. 00000168h: 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 ; ..java/io/PrintS 00000178h: 74 72 65 61 6D 0C 00 26 00 15 01 00 07 70 72 69 ; tream..&.....pri 00000188h: 6E 74 6C 6E 01 00 0A 53 6F 75 72 63 65 46 69 6C ; ntln...SourceFil 00000198h: 65 01 00 10 50 72 69 6E 74 53 74 72 69 6E 67 2E ; e...PrintString. 000001a8h: 6A 61 76 61 ; java
前面两个字节 0029 表示常量池的大小 40 个,常量池的内容会被后面所有部分使用到。
第1个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=0002,查看 2 个常量
第2个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16 个字节。内容为 test/PrintString
第3个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=0004,查看 4 号常量
第4个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16 个字节。内容为 java/lang/Object
第5个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0003 说明占用后面的 3 个字节。内容为 str
第6个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18 个字节。内容为 Ljava/lang/String;
第7个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0006 说明占用后面的 6 个字节。内容为 <init>
第8个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0003 说明占用后面的 4 个字节。内容为 ()V
第9个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0004 说明占用后面的 4 个字节。内容为 Code
第10个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0003,name_and_type_index=000B。内容为 java/lang/Object 的 init 方法
第11个常量 0C 是一个 constant_nameAndType 类型,后面的 4 个字节属于它,name_index=0007,descriptor_index=0008,内容为<init>()V
第12个常量 08 是一个 constant_String 类型,后面的 2 个字节属于它,000D 查看13号常量池
第13个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 00 说明占用后面的 0 个字节。内容为空
第14个常量 09 是一个 constant_Fieldref 类型,后面的 4 个字节表示它占用的字节数,class_index=0001 , name_and_type_index =000F。内容为 test/PrintString 的 str
第15个常量 0C 是一个 constant_nameAndType 类型,后面的 4 个字节属于它,name_index=0005,descriptor_index=0006,。内容为 Ljava/lang/String; str
第16个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,000F 说明占用后面的 15 个字节。内容为 LineNumberTable
第17个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18个字节。内容为 LocalVariableTable
第18个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0004 说明占用后面的 4个字节。内容为 this
第19个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0012 说明占用后面的 18个字节。内容为 Ltest/PrintString;
第20个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0009 说明占用后面的 9个字节。内容为 setString;)
第21个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0015 说明占用后面的 21个字节。内容为 (Ljava/lang/String;)V
第22个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0001 说明占用后面的 1个字节。内容为 s
第23个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0009 说明占用后面的 9个字节。内容为 getString
第24个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0014 说明占用后面的 20个字节。内容为 ()Ljava/lang/String;
第25个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0005 说明占用后面的 20个字节。内容为 print
第26个常量 09 是一个 constant_Fieldref 类型,根据它的定义后面四个字节属于它,class_index=001B 查看27号常量, name_and_type_index = 001D 查看29号常量
第27个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=001C,查看28号常量
第28个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0010 说明占用后面的 16个字节。内容为 java/lang/System
第29个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=001E 查看30号常量,descriptor_index=001F 查看31号常量
第30个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 03 说明占用后面的 3个字节。内容为 out
第31个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 15 说明占用后面的 21个字节。内容为Ljava/io/PrintStream;
第32个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0001,name_and_type_index=0021
第33个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=0017 查看23号常量,descriptor_index=0018 查看24号常量
第34个常量 0A 是一个 constant_methodref 类型,后面的 4 个字节属于它,class_index=0023 查看35号常量,name_and_type_index=0025 查看37号常量
第35个常量 07 是一个 constant_Class 类型,后面的 2 个字节属于它,name_index=00 24,查看36号常量
第36个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 13 说明占用后面的 19个字节。内容为 java/io/PrintStream
第37个常量 0C 是一个 constant_nameAndType 类型,根据它的定义后面的4个字节属于它,name_index=0026 查看38号常量,descriptor_index=0015 查看21号常量
第38个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,0007 说明占用后面的 7个字节。内容为 println
第39个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,000A 说明占用后面的 10个字节。内容为 SourceFile
第40个常量 01 是一个 constant_UTF8 类型,后面的 2 个字节表示它占用的字节数,00 10 说明占用后面的 16个字节。内容为 PrintString.java
第三部分占用 8 个字节:
000001ach: 00 21 00 01 00 03 00 00 ; .!......
前面 2 个字节 0021 表示类本身的修饰符
中间 2 个字节 0001 表示类本身的常量表索引,内容为 test/PrintString
下面 2 个字节 0003 表示父类的常量表索引,内容为 java/lang/Object
最后 2 个字节 0000 表示类实现的接口数量,此类未实现接口
第四部分是类文件是比较复杂的部分,它分为两个部分:属性区和方法区。
先看属性区:
000001b4h: 00 01 00 02 00 05 00 06 00 00 ; ..........
前面 2 个字节 0001 表示类声明了一个属性
下面 2 个字节 0002 表示后面 2 个字节是属性的描述
下面 2 个字节 0005 表示属性名称的常量池索引,5号常量的信息 str
下面 2 个字节 0006 表示属性描述的常量池索引,6号常量的信息 Ljava/lang/String;
最后 2 个字节 0000 表示属性本身的属性的数量
再看方法区:
000001beh: 00 04 00 01 00 07 00 08 00 01 00 09 00 00 00 3D ; ...............= 000001ceh: 00 02 00 01 00 00 00 0B 2A B7 00 0A 2A 12 0C B5 ; ........*?.*..? 000001deh: 00 0E B1 00 00 00 02 00 10 00 00 00 0E 00 03 00 ; ..?............ 000001eeh: 00 00 03 00 04 00 05 00 0A 00 03 00 11 00 00 00 ; ................ 000001feh: 0C 00 01 00 00 00 0B 00 12 00 13 00 00 // 此处为第一个方法和第二个方法的交界 00 01 00 ; ................ 0000020eh: 14 00 15 00 01 00 09 00 00 00 3E 00 02 00 02 00 ; ..........>..... 0000021eh: 00 00 06 2A 2B B5 00 0E B1 00 00 00 02 00 10 00 ; ...*+?.?...... 0000022eh: 00 00 0A 00 02 00 00 00 08 00 05 00 09 00 11 00 ; ................ 0000023eh: 00 00 16 00 02 00 00 00 06 00 12 00 13 00 00 00 ; ................ 0000024eh: 00 00 06 00 16 00 06 00 01 00 01 00 17 00 18 00 ; ................ 0000025eh: 01 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A ; ....../........* 0000026eh: B4 00 0E B0 00 00 00 02 00 10 00 00 00 06 00 01 ; ?.?........... 0000027eh: 00 00 00 0C 00 11 00 00 00 0C 00 01 00 00 00 05 ; ................ 0000028eh: 00 12 00 13 00 00 00 01 00 19 00 08 00 01 00 09 ; ................ 0000029eh: 00 00 00 39 00 02 00 01 00 00 00 0B B2 00 1A 2A ; ...9........?.* 000002aeh: B6 00 20 B6 00 22 B1 00 00 00 02 00 10 00 00 00 ; ? ?"?........ 000002beh: 0A 00 02 00 00 00 10 00 0A 00 11 00 11 00 00 00 ; ................ 000002ceh: 0C 00 01 00 00 00 0B 00 12 00 13 00 00 ; .............
前面 2 个字节 0004 表示类声明四个方法(注意:包含一个隐含的构造方法 init)
下面 2 个字节 0001 表示是一个ACC_PUBLIC 的方法
下面 2 个字节 0007 表示 name_index,表示常量池中第7个常量为<init>,
下面 2 个字节 0008 表示desciptor_index,表示常量池第8个常量为 ()V
下面 2 个字节 0001 表示attribute_count,表示有1个attribute
下面 2 个字节 0009 表示code_attribute,查看第9号常量池为Code
下面4 个字节 0000003D 表示后面的 61 个字节属于这个属性
下面 2 个字节 0002 表示 max_stack 该方法执行的时候操作数栈最大的长度,这里表示操作数栈的长度为1
下面 2 个字节 0001 表示 max_locals 该方法局部变量所需要的空间的长度
下面 4 个字节 00 00 00 0B 表示code_length 即后面的 11 个字节为虚拟机字节码内容
代码的 11 个字节:2A B7 00 0A 2A 12 0C B5 00 0E B1
2A :aload_0 表示将第一个引用类型本地变量推送至栈顶
B7 :invokespecial 调用超类方法,后面的 000A,查看10号常量池 表示调用超类的init方法
2A :aload_0 表示将第一个引用类型本地变量推送至栈顶
12 :ldc 表示将一个常量池压入操作栈,后面的 0C 便是这个操作数,查看第13号常量池,为空
B5 :putfield 将操作栈的值赋给类变量,后面2个字节 000E 代表类变量的常量池索引,为 Ljava/lang/String; str
B1 : return ;返回void
这里我们可以知道类变量的初始化位置。
代码的 11 个字节以后
下面 2 个字节 0000 表示exception_table_length=0;也就是说没有异常处理
下面 2 个字节 0002 表示attributes_count=2,接下来有两个attribute_info 的结构
下面 2 个字节 0010 表示 attribute_name_index,查看10号常量池,为 LineNumberTable
下面 4 个字节 00 00 00 0E 表示attribute_length=14
下面 14 个字节是 LineNumberTable 的属性
下面 2 个字节 0011 表示 attribute_name_index,查看10号常量池,为 LocalVariableTable
下面 4 个字节 00 00 00 0C 表示attribute_length=12
下面 12 个字节是 LocalVariableTable 的属性
以上只是第一个方法的说明,后面的方法不再详细说明。
第五部分通常不是很重要,大概了解一下:
000002dbh: 00 01 00 27 00 00 00 02 00 28 ; ...'.....(
前面 2 个字节 0001 表示接下去有一个 attributes_info 结构,该结构占用 8 个字节
下面 2 个字节 0027 表示属性的名称的常量池索引,39 号常量的信息(属性的名称)为 SourceFile
下面 4 个字节 00000002 表示属性的长度,因为格式固定,所长度为 2
最后 2 个字节 0028 表示 sourcefile_index 的常量池索引, 40 号常量的信息(源文件的名称)为 PrintString.java
以上只是对一个结构简单的类作了一个简要说明,实际情况会复杂很多。详细内容大家可以参考《深入 Java 虚拟机(第二版)》
发表评论
-
windows 下配置 nginx + tomcat + memcached 集群
2014-06-11 16:33 1793前几天介绍了一下 memcached-session-filt ... -
让 memcached-session-filter 摆脱 spring 和 Java 序列化接口
2014-06-09 16:03 7146memcached-session-filter 项目是在 j ... -
关于 tomcat 集群中 session 共享的三种方法
2014-05-20 11:01 2317前两种均需要使用 memcached 或 redis 存储 ... -
简洁实用的免费的 FCKEditor 编辑器(2)
2014-04-09 09:18 1269前面已经下载了软件,并对于最基本的使用和支持文件上传讲了一下。 ... -
分布式版本管理 GIT 超酷教程
2014-04-08 09:09 645直接上链接: http://www.liaoxuefeng.c ... -
简洁实用的免费的 FCKEditor 编辑器(1)
2014-04-07 15:39 1751CKEditor 功能越来越强,界面也越来越靓,但收费总是让人 ... -
Eclipse 彻底禁用 JavaScript 验证
2014-04-05 13:34 1414我用的版本: Version: Indigo Service ... -
将旧版本的 eWebEditor 从 ASP 版改造为 JSP 版
2014-04-04 18:40 1769之前一个旧网站项目改版,发布系统的编辑器使用的 eWebEdi ... -
JavaRebel 关于 noverify 和 javaagent 参数的使用
2011-08-15 13:21 9158一般情况下,使用 JavaRebel 时都配置两个 JVM ... -
Eclipse 开发过程中利用 JavaRebel 提高效率
2011-08-13 09:43 1335Eclipse 是 Java 语言开发过程中的利器,相比较 M ... -
Mina 基本使用和常用类的介绍
2011-08-04 09:02 2259Mina 的全称是 Multipurpose ... -
FreeMarker 的两个应用实例
2011-07-18 12:26 2313FreeMarker 是一个 Java ... -
Java 的静态内部类使用
2011-07-11 11:02 1519Java 的内部类一般情况下很少使用,声明为 static 的 ... -
使用 UltraEdit 复制十六进制代码
2011-07-10 21:34 4362用UltraEdit 编辑文件时,常要用到查找、替换、复制和粘 ... -
深入学习 Lucene 3.0 索引段
2011-07-07 13:07 2007Lucene索引index由若干段(segment)组成,每一 ... -
Websphere MQ 学习笔记
2011-06-26 12:44 2438通信技术 MQI(Message Queue ... -
Java 程序的优化笔记
2011-06-15 08:59 1535系统的优化是一个比较宽泛的话题,涉及到硬件、软件和网络的优化等 ... -
XP 下 Java 本地接口调用 MinGW 编译的动态库
2011-06-13 13:26 1411Java 程序可以“一次编写,到处运行”,原因是它运行在 J ... -
JAVA 类中 serialVersionUID 的作用
2011-06-10 21:25 1370通常在继承 Serializable 接口的类,Eclips ... -
详解 Java 语言中 float 类型的运算
2011-06-08 13:56 5056【注】本文参考了网上的部分资料加上本人水平有限,存在错误在所 ...
相关推荐
5. javap:这个工具用于反编译.class文件,查看Java类文件的内部结构。它可以显示类的方法、字段等信息。例如,使用命令`javap -l MyClass`可以查看类MyClass的详细信息。 6. jdb.exe:Java调试器,用于调试Java...
标题中的“将jar包转成.java的源码的工具”是指一种可以反编译Java字节码(.class文件)并将其转换为源代码(.java文件)的软件工具。在Java开发中,有时我们需要查看或理解已编译的jar包内部的工作原理,这种工具就...
Java的Class文件是Java源代码经过编译器处理后的二进制表示,它包含了程序的结构和指令,但不直接可读。理解如何反编译Class文件对于学习、调试和分析Java程序至关重要。本文将深入探讨Java Class文件反编译的相关...
根据提供的文件信息,我们可以整理出以下关键知识点,主要聚焦于Java编程语言的基础学习与实践,以及构建工具Ant的使用。 ### Java基础知识 #### 单元测试基础 在Java开发过程中,单元测试是确保代码质量的重要...
1. **字节码**: Java源代码(.java文件)通过编译器编译成字节码(.class文件),这是一种中间语言,独立于特定平台,可以在任何支持Java的平台上运行,由Java虚拟机(JVM)解释执行。 2. **NoClassDefFoundError**: 这个...
Java反编译工具是开发人员用来查看已编译的Java字节码(.class文件)源代码的软件。这些工具对于理解第三方库的工作原理、学习API的内部实现、调试问题或者在没有源代码的情况下进行逆向工程至关重要。本文将详细...
在配置过程中可能会出现错误,如“javac”不是内部或外部命令,也不是可运行的程序或批处理文件,这是因为 path 的路径配置不正确。 二、JAVA 程序的运行机制和运行过程 JAVA 程序是通过编写、编译、运行三个步骤...
53. 描述一下JVM加载class文件的原理机制? 30 54. socket编程 30 54.1. 什么是TCP/IP、UDP? 30 54.2. Socket在哪里呢? 31 54.3. Socket是什么呢? 32 54.4. socket的实现步骤 37 55. Servlet 38 55.1. Servlet工作...
### Java编码规范文档知识点 #### 1. 概述 - **内容**:本文档主要涵盖了Java编程语言的编码规范,包括命名规则、注释规范、排版规范以及数据库相关的命名与格式化等内容。 - **编写目的**:制定一套统一的编码规范...
Java的Javac编译器负责这一过程,将.java文件转化为.class文件。 3. **调试工具**:调试是软件开发中的重要环节。JC工具可能提供断点设置、单步执行、变量查看、调用堆栈分析等功能,帮助开发者找出和修复代码中的...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
- **class**: 在Java中,`class`关键字用于声明一个类。类是面向对象编程的基本单元,它定义了一组属性(成员变量)和方法(成员函数),这些共同构成了一个特定类型的对象。例如: ```java public class Person ...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
75、描述一下JVM加载class文件的原理机制? 56 76、heap和stack有什么区别。 57 77、GC是什么? 为什么要有GC? 57 78、垃圾回收的优点和原理。并考虑2种回收机制。 57 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器...