【JVM学习笔记】字节码文件结构实例
上一篇笔记的内容大部分没有实际动手操作,因此决定完成这个完整的练习并记录下来。
另注,idea环境下有jclasslib插件用于更好的查看类似于javap结果的内容。
源代码如下:
package com.learn.jvm; /**
* @Description
* @date 2019/09/05 16:31
*/
public class Test {
String str = "Welcome";
private int x = 5;
public static Integer num = 10; public static void main(String[] args) {
Test test = new Test();
test.setX(8);
num = 20;
} private synchronized void setX(int x) {
this.x = x;
} private void test1(String str) {
// 注意,这里会对str对象本身加锁,如果str是"abc",则是对"abc"这个字符串对象加锁,如果str是"def",则是对"def"这个对象加锁
// 所以实际上这种写法无法实现正常的加锁功能,只是了学习字节码而举的例子
synchronized (str) {
System.out.println("hello world");
}
} private synchronized static void test2(){}
}
编译后在Windows平台下进入wsl,使用命令vim Test.class,然后输入:%!xxd就可以看到16进制代码。
也可以使用Linux下的xxd命令,将二进制信息转换为16进制数据,使用方式为 xxd Test.class Test.txt,
生成的HelloWorld.txt与通过:%!xxd
是一样的(实际操作时返现在结尾处略有不同,生成txt文件少了1个字节)
00000000: cafe babe 0000 0033 0046 0a00 0d00 2d08 .......3.F....-.
00000010: 002e 0900 0500 2f09 0005 0030 0700 310a ....../....0..1.
00000020: 0005 002d 0a00 0500 320a 0033 0034 0900 ...-....2..3.4..
00000030: 0500 3509 0036 0037 0800 380a 0039 003a ..5..6.7..8..9.:
00000040: 0700 3b01 0003 7374 7201 0012 4c6a 6176 ..;...str...Ljav
00000050: 612f 6c61 6e67 2f53 7472 696e 673b 0100 a/lang/String;..
00000060: 0178 0100 0149 0100 036e 756d 0100 134c .x...I...num...L
00000070: 6a61 7661 2f6c 616e 672f 496e 7465 6765 java/lang/Intege
00000080: 723b 0100 063c 696e 6974 3e01 0003 2829 r;...<init>...()
00000090: 5601 0004 436f 6465 0100 0f4c 696e 654e V...Code...LineN
000000a0: 756d 6265 7254 6162 6c65 0100 124c 6f63 umberTable...Loc
000000b0: 616c 5661 7269 6162 6c65 5461 626c 6501 alVariableTable.
000000c0: 0004 7468 6973 0100 144c 636f 6d2f 6c65 ..this...Lcom/le
000000d0: 6172 6e2f 6a76 6d2f 5465 7374 3b01 0004 arn/jvm/Test;...
000000e0: 6d61 696e 0100 1628 5b4c 6a61 7661 2f6c main...([Ljava/l
000000f0: 616e 672f 5374 7269 6e67 3b29 5601 0004 ang/String;)V...
00000100: 6172 6773 0100 135b 4c6a 6176 612f 6c61 args...[Ljava/la
00000110: 6e67 2f53 7472 696e 673b 0100 0474 6573 ng/String;...tes
00000120: 7401 0004 7365 7458 0100 0428 4929 5601 t...setX...(I)V.
00000130: 0005 7465 7374 3101 0015 284c 6a61 7661 ..test1...(Ljava
00000140: 2f6c 616e 672f 5374 7269 6e67 3b29 5601 /lang/String;)V.
00000150: 000d 5374 6163 6b4d 6170 5461 626c 6507 ..StackMapTable.
00000160: 0031 0700 3c07 003b 0700 3d01 0005 7465 .1..<..;..=...te
00000170: 7374 3201 0008 3c63 6c69 6e69 743e 0100 st2...<clinit>..
00000180: 0a53 6f75 7263 6546 696c 6501 0009 5465 .SourceFile...Te
00000190: 7374 2e6a 6176 610c 0014 0015 0100 0757 st.java........W
000001a0: 656c 636f 6d65 0c00 0e00 0f0c 0010 0011 elcome..........
000001b0: 0100 1263 6f6d 2f6c 6561 726e 2f6a 766d ...com/learn/jvm
000001c0: 2f54 6573 740c 0020 0021 0700 3e0c 003f /Test.. .!..>..?
000001d0: 0040 0c00 1200 1307 0041 0c00 4200 4301 .@.......A..B.C.
000001e0: 000b 6865 6c6c 6f20 776f 726c 6407 0044 ..hello world..D
000001f0: 0c00 4500 2301 0010 6a61 7661 2f6c 616e ..E.#...java/lan
00000200: 672f 4f62 6a65 6374 0100 106a 6176 612f g/Object...java/
00000210: 6c61 6e67 2f53 7472 696e 6701 0013 6a61 lang/String...ja
00000220: 7661 2f6c 616e 672f 5468 726f 7761 626c va/lang/Throwabl
00000230: 6501 0011 6a61 7661 2f6c 616e 672f 496e e...java/lang/In
00000240: 7465 6765 7201 0007 7661 6c75 654f 6601 teger...valueOf.
00000250: 0016 2849 294c 6a61 7661 2f6c 616e 672f ..(I)Ljava/lang/
00000260: 496e 7465 6765 723b 0100 106a 6176 612f Integer;...java/
00000270: 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 lang/System...ou
00000280: 7401 0015 4c6a 6176 612f 696f 2f50 7269 t...Ljava/io/Pri
00000290: 6e74 5374 7265 616d 3b01 0013 6a61 7661 ntStream;...java
000002a0: 2f69 6f2f 5072 696e 7453 7472 6561 6d01 /io/PrintStream.
000002b0: 0007 7072 696e 746c 6e00 2100 0500 0d00 ..println.!.....
000002c0: 0000 0300 0000 0e00 0f00 0000 0200 1000 ................
000002d0: 1100 0000 0900 1200 1300 0000 0600 0100 ................
000002e0: 1400 1500 0100 1600 0000 4200 0200 0100 ..........B.....
000002f0: 0000 102a b700 012a 1202 b500 032a 08b5 ...*...*.....*..
00000300: 0004 b100 0000 0200 1700 0000 0e00 0300 ................
00000310: 0000 0800 0400 0900 0a00 0a00 1800 0000 ................
00000320: 0c00 0100 0000 1000 1900 1a00 0000 0900 ................
00000330: 1b00 1c00 0100 1600 0000 5700 0200 0200 ..........W.....
00000340: 0000 17bb 0005 59b7 0006 4c2b 1008 b700 ......Y...L+....
00000350: 0710 14b8 0008 b300 09b1 0000 0002 0017 ................
00000360: 0000 0012 0004 0000 000e 0008 000f 000e ................
00000370: 0010 0016 0011 0018 0000 0016 0002 0000 ................
00000380: 0017 001d 001e 0000 0008 000f 001f 001a ................
00000390: 0001 0022 0020 0021 0001 0016 0000 003e ...". .!.......>
000003a0: 0002 0002 0000 0006 2a1b b500 04b1 0000 ........*.......
000003b0: 0002 0017 0000 000a 0002 0000 0014 0005 ................
000003c0: 0015 0018 0000 0016 0002 0000 0006 0019 ................
000003d0: 001a 0000 0000 0006 0010 0011 0001 0002 ................
000003e0: 0022 0023 0001 0016 0000 0085 0002 0004 .".#............
000003f0: 0000 0017 2b59 4dc2 b200 0a12 0bb6 000c ....+YM.........
00000400: 2cc3 a700 084e 2cc3 2dbf b100 0200 0400 ,....N,.-.......
00000410: 0e00 1100 0000 1100 1400 1100 0000 0300 ................
00000420: 1700 0000 1200 0400 0000 1a00 0400 1b00 ................
00000430: 0c00 1c00 1600 1d00 1800 0000 1600 0200 ................
00000440: 0000 1700 1900 1a00 0000 0000 1700 0e00 ................
00000450: 0f00 0100 2400 0000 1800 02ff 0011 0003 ....$...........
00000460: 0700 2507 0026 0700 2700 0107 0028 fa00 ..%..&..'....(..
00000470: 0400 2a00 2900 1500 0100 1600 0000 1900 ..*.)...........
00000480: 0000 0000 0000 01b1 0000 0001 0017 0000 ................
00000490: 0006 0001 0000 001f 0008 002a 0015 0001 ...........*....
000004a0: 0016 0000 0021 0001 0000 0000 0009 100a .....!..........
000004b0: b800 08b3 0009 b100 0000 0100 1700 0000 ................
000004c0: 0600 0100 0000 0b00 0100 2b00 0000 0200 ..........+.....
000004d0: 2c0a ,.
javap -verbose -p 的输出结果如下(不待参数p则不会显示私有成员)
D:\workspace-learn\common-learn\learn-classloader\target\classes\com\learn\jvm>javap -verbose -p Test
警告: 二进制文件Test包含com.learn.jvm.Test
Classfile /D:/workspace-learn/common-learn/learn-classloader/target/classes/com/learn/jvm/Test.class
Last modified 2019-9-5; size 1233 bytes
MD5 checksum d7a9950df521ddf0ee5c7b76c1a25796
Compiled from "Test.java"
public class com.learn.jvm.Test
SourceFile: "Test.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#45 // java/lang/Object."<init>":()V
#2 = String #46 // Welcome
#3 = Fieldref #5.#47 // com/learn/jvm/Test.str:Ljava/lang/String;
#4 = Fieldref #5.#48 // com/learn/jvm/Test.x:I
#5 = Class #49 // com/learn/jvm/Test
#6 = Methodref #5.#45 // com/learn/jvm/Test."<init>":()V
#7 = Methodref #5.#50 // com/learn/jvm/Test.setX:(I)V
#8 = Methodref #51.#52 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#9 = Fieldref #5.#53 // com/learn/jvm/Test.num:Ljava/lang/Integer;
#10 = Fieldref #54.#55 // java/lang/System.out:Ljava/io/PrintStream;
#11 = String #56 // hello world
#12 = Methodref #57.#58 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #59 // java/lang/Object
#14 = Utf8 str
#15 = Utf8 Ljava/lang/String;
#16 = Utf8 x
#17 = Utf8 I
#18 = Utf8 num
#19 = Utf8 Ljava/lang/Integer;
#20 = Utf8 <init>
#21 = Utf8 ()V
#22 = Utf8 Code
#23 = Utf8 LineNumberTable
#24 = Utf8 LocalVariableTable
#25 = Utf8 this
#26 = Utf8 Lcom/learn/jvm/Test;
#27 = Utf8 main
#28 = Utf8 ([Ljava/lang/String;)V
#29 = Utf8 args
#30 = Utf8 [Ljava/lang/String;
#31 = Utf8 test
#32 = Utf8 setX
#33 = Utf8 (I)V
#34 = Utf8 test1
#35 = Utf8 (Ljava/lang/String;)V
#36 = Utf8 StackMapTable
#37 = Class #49 // com/learn/jvm/Test
#38 = Class #60 // java/lang/String
#39 = Class #59 // java/lang/Object
#40 = Class #61 // java/lang/Throwable
#41 = Utf8 test2
#42 = Utf8 <clinit>
#43 = Utf8 SourceFile
#44 = Utf8 Test.java
#45 = NameAndType #20:#21 // "<init>":()V
#46 = Utf8 Welcome
#47 = NameAndType #14:#15 // str:Ljava/lang/String;
#48 = NameAndType #16:#17 // x:I
#49 = Utf8 com/learn/jvm/Test
#50 = NameAndType #32:#33 // setX:(I)V
#51 = Class #62 // java/lang/Integer
#52 = NameAndType #63:#64 // valueOf:(I)Ljava/lang/Integer;
#53 = NameAndType #18:#19 // num:Ljava/lang/Integer;
#54 = Class #65 // java/lang/System
#55 = NameAndType #66:#67 // out:Ljava/io/PrintStream;
#56 = Utf8 hello world
#57 = Class #68 // java/io/PrintStream
#58 = NameAndType #69:#35 // println:(Ljava/lang/String;)V
#59 = Utf8 java/lang/Object
#60 = Utf8 java/lang/String
#61 = Utf8 java/lang/Throwable
#62 = Utf8 java/lang/Integer
#63 = Utf8 valueOf
#64 = Utf8 (I)Ljava/lang/Integer;
#65 = Utf8 java/lang/System
#66 = Utf8 out
#67 = Utf8 Ljava/io/PrintStream;
#68 = Utf8 java/io/PrintStream
#69 = Utf8 println
{
java.lang.String str;
flags: private int x;
flags: ACC_PRIVATE public static java.lang.Integer num;
flags: ACC_PUBLIC, ACC_STATIC public com.learn.jvm.Test();
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String Welcome
7: putfield #3 // Field str:Ljava/lang/String;
10: aload_0
11: iconst_5
12: putfield #4 // Field x:I
15: return
LineNumberTable:
line 8: 0
line 9: 4
line 10: 10
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this Lcom/learn/jvm/Test; public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #5 // class com/learn/jvm/Test
3: dup
4: invokespecial #6 // Method "<init>":()V
7: astore_1
8: aload_1
9: bipush 8
11: invokespecial #7 // Method setX:(I)V
14: bipush 20
16: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
19: putstatic #9 // Field num:Ljava/lang/Integer;
22: return
LineNumberTable:
line 14: 0
line 15: 8
line 16: 14
line 17: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 args [Ljava/lang/String;
8 15 1 test Lcom/learn/jvm/Test; private synchronized void setX(int);
flags: ACC_PRIVATE, ACC_SYNCHRONIZED
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #4 // Field x:I
5: return
LineNumberTable:
line 20: 0
line 21: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/learn/jvm/Test;
0 6 1 x I private void test1(java.lang.String);
flags: ACC_PRIVATE
Code:
stack=2, locals=4, args_size=2
0: aload_1
1: dup
2: astore_2
3: monitorenter
4: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #11 // String hello world
9: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_2
13: monitorexit
14: goto 22
17: astore_3
18: aload_2
19: monitorexit
20: aload_3
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
LineNumberTable:
line 26: 0
line 27: 4
line 28: 12
line 29: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this Lcom/learn/jvm/Test;
0 23 1 str Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/learn/jvm/Test, class java/lang/String, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4 private static synchronized void test2();
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 31: 0 static {};
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 10
2: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: putstatic #9 // Field num:Ljava/lang/Integer;
8: return
LineNumberTable:
line 11: 0
}
同时贴出Java字节码整体结构图便于对比查看
以下开始逐个字节进行分析(仅分析一部分重要字节和代表性的字节,因为太多了)
第1~4个字节:ca fe ba be 为魔数
第5~6个字节:00 00 代表 minor version ,值为0
第7~8个字节:00 33 代表 major version ,值为51
第9~10个字节:00 46 ,换算成十进制为 4*16+6 = 70 ,代表常量池中共有69个常量(除去一个为null保留的常量),我们通过查看上面 avap -verbose -p打印出来的信息可以验证,确实是这样的
接下来是具体的常量分析,所以将常量池中的数据类型结构图,和相关知识要点贴出来
要点:
- 使用java -verbose 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息
- 魔数:所有.class字节码文件的前四个字节都是魔数,,魔数值为固定值:0xCAFEBABE
- 魔数之后的4个字节代码版本号,前2个字节代表次版本号(minor version),后2个字节代表主版本号(major version),以上图为例,次版本号为0,主版本号为52,所以,该文件的版本号是1.8.0
- 常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个Java类定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法与变量信息,都是存储在常量池中。常量池中主要存储两类常量:字面量与符号应用。字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等
- 常量池的总体结构:Java类所对应的常量池主要由“常量池数量”与“常量池数组”这两部分共同构成。常量池数量紧跟在主版本号后面(也就是第9个字节开始),占据2个字节;常量池数组则紧跟在常量池数量之后。常量池数组与一般的数组不同的是,常量池数组中不同的原素的类型、结构都是不同的,长度当然也就不同,但是,每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。以上图为例,第9到第10个字节连起来时 0018,即十进制数字的24,意即后面有24个常量。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池”的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量池数组(也叫常量表)中,这个常量就对应null值,所以常量池的索引从1而非0开始。以上图为例显示有24个常量,但是根据 java -verbose的输出结果,显示只有23个常量。
- 在JVM规范种,每个变量/字段都有描述信息,描述信息的主要作用是描述字段的数据类型、方法的参数列表(包括数量、类型与顺序)与返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,而对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母,如下所示:B - byte ,C - char,D - double,F - float, I - int,J - long,S - short,Z - boolean,V - void,L - 对象类型,如Ljava/lang/String;
- 对于数组类型来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I,String[][]被记录为[[Ljava/lang/String;
- 用描述符来描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之类,如方法String getRealNameByIdAndName(int id, String name),表示为(I,Ljava/lang/String;)Ljava/lang/String;
(以下为第1个常量)
第11个字节:0a,换算成十进制为 10,代表该常量是上图中的 CONSTANT_Methodref_info 类型
第12~13个字节(表示这个方法时由哪个类所定义的):00 0d,换算成十进制为13,根据本文开头javap -verbose -p 的结果,最终可以看到这个索引项指向一个Class类型,值为 "java/lang/Object"
第14~15个字节(对方法本身的描述):00 2d,换算成十进制为45,从常量池中找到结果为 "<init>":()V ,即该方法名称为<init> (注:也就是构造方法),参数列表为空,返回值类型为void
(以下为第2个常量)
第16个字节:08,代表上图中的 CONSTANT_String_info 类型
第17~第18个字节:00 2e,十进制为46,引用的是字符串常量"Welcome"
(以下为第3个常量)
第19个字节:09,代表上图中的 CONSTANT_Fieldref_info 类型
第20~21个字节:00 05,引用值为 "com/learn/jvm/Test",表示该字段是在Test类中声明的
第22~23个字节:00 2f,十进制为47,值为 str:Ljava/lang/String; 表示该字段名为str,类型是一个String对象
(以下为第4个常量)
第24个字节:09,和第三个常量类似,此处省略
(以下为第5个常量)
第29个字节:07,代表上图中的 CONSTANT_Class_info 类型
第30~31个字节:00 31,十进制为49,引用值为 "com/learn/jvm/Test"
(以下为第6个常量)
第32个字节:0a,与第1个常量类似,此处省略
(以下为第7个常量)
第37个字节:0a,省略
(以下为第8个变量)
第42个字节:0a,是定义在java/lang/Integer类中的方法,方法名为 valueOf,参数列表为int,返回一个Ljava/lang/Integer对象。之所以存在这个方法,是因为我们在程序中将一个int值赋给了Integer,导致自动装箱。
(以下为第9个变量)
第47个字节:09,省略
······
常量池到此结束
Access_Flag访问标志部分
访问标志信息包括该Class文件是类还是接口,是否被定义为public,是否是abstract,如果是类,是否被声明为final
上图缺少了0x0002代表 ACC_PRIVATE
(以下为Access Flags)
第??~??个字节:00 21 ,对应于上图就是 ACC_PUBLIC | ACC_SUPER (即这两者的并集)
The Class Name
第??~??个字节:00 05,对应常量池中索引为5的"com/learn/jvm/Test"
Super Class Name
第??~??个字节:00 0d,对应常量池中的索引为13的"java/lang/Object"
Interfaces
第??~??个字节:00 00,代表接口数量为0,我们的类没有实现任何接口
Fields (2+N个字节)
接下就到了字段表集合了,相关知识如下:
字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。
上面两张图是一个意思,都是介绍了字段表集合的结构
第??~??个字节:00 03,代表有3个成员变量
(以下为第1个成员变量)
access_flags 为 00 00 ,代表默认访问级别(即 "default")
name_index 为 00 0e,即常量池中索引值为14指向的常量 "str"
descriptor_index 为 00 0f,在常量池中找到为 "Ljava/lang/String;"
attributes_count 为 00 00,属性数量为0,表示没有属性 (备注:属性是什么??)
由于属性数量为0,attribute_info也就没有了
(以下为第2个成员变量)
0002 ,代表访问修饰符为 private
0010,在常量池中找到值为 "x" ,表示变量名为 x
0011,在常量池中找到值为 "I",表明这个成员变量的类型为 int
0000,表示属性数量为0
(以下为第三个成员变量)
0009,代表访问修饰符为 public static (上图中找不到0009的解释,但是在jclasslib插件中可以看到是public static)
0012,在常量池中找到为num,表示变量名为num
0013,在常量池找到为 Ljava/lang/Integer; 表示变量类型
0000,属性数量为0
Method 部分 ,前两个字节表示方法数量
0006,表示有6个方法,从下图jclasslib的结果中也可以看出确实是6个,其中最后一个方法是对静态信息的初始化,包括静态的成员变量和静态块
下面开始逐个分析这些方法的十六进制代码,先贴出方法表相关的知识:
其中 attribute_info,这个属性跟方法里面的成员变量不是一回事,这个属性是归属于这个方法本身的。javac在编译好一个字节码文件之后,会给相应的方法额外增加一些attribute信息,这些attribute信息就描述了这个方法,比如方法的执行字节码是什么,方法的行号表是什么,局部变量表是什么,attribute_count就表明这些属性一共有多少个。attribute_info结构如下
注意,上图中的 u1 info[attribute_length]其实放在这里这种写法是很容易造成混淆的,其实它的意思仅仅是表示有attribute_length个info,每个info是一个字节。我在这里晕了半天,搞懂了,所以记录一下。
注意,其中 u1 code[code_length]表示的是一共有code_length个code,每个code是1个字节。据里来说,u4 code_length的值是10,那么说明接下来10个字节是虚拟机真正执行的质量,即助记符,u1 code[10]就表示这个意思。这张图这个地方是很容易造成误解的,所以我理解之后做个这个备注进行说明。
(第一个方法)
0001:代表访问修饰符为 ACC_PUBLIC
0014:方法名字索引,十进制为20,在常量池中找到为<init>
0015:方法描述符,十进制为21,在常量池中找到为()V,表示参数个数为0,返回类型为void
0001:表示属性个数为1个
(以下为第1个方法的attribute_info)
0016:属性名,十进制为22,在常量池中找到值为"Code",表明从这里开始,是一个Code类型的属性,Code的结构参考上面的几张图
00000042:属性长度,表示一共66个字节,即接下来66个字节就是这个方法真正所执行的代码对应的指令
(以下为第1个方法的attribute_info的Code属性(即Code_attribute除去名字和长度字段的内容)的内容)
0002:(max_stack) ,最大操作数栈深度为2
0001:(max_locals),
00000010:(code_length),接下来16个字节就是该方法对应的真正的虚拟机将要执行的指令,这些十六进制代码对应了各自的助记符
-----------------------------------------------------------------------------------------------
手工翻译的工作暂时到此为止吧,有点单调乏味,下面用jclasslib插件来看其他一些需要学习的点
-----------------------------------------------------------------------------------------------
1. 构造函数
从上图中可以看出,实际上我们对str和x这两个变量的赋值是在自动生成的构造方法里完成的。这就有一个疑问,上面这种情况是在我们没有自定义构造函数的情况下发生的,如果我们定义了构造函数,情况又将如何?
可以看出,编译器将x和str的赋值合并到了我们自定义的构造方法里面
如果我们有两个构造方法,又是什么情况呢?
试了一下,结果发现如上图,生成的字节码中,两个构造函数里均有对x和str赋值的相关代码
2.关于锁
先逐个解释这些助记符(助记符文档 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.dup)
aload_1:将局部变量表中的1号变量的值压入栈顶,即将str变量压入栈顶,注意,0号参数是this,所以str只能是1号变量
dup:复制操作数堆栈上的顶部值,并将复制 的值压入操作数堆栈。
先解释到这里吧,感觉还需要学习。
【JVM学习笔记】字节码文件结构实例的更多相关文章
- JVM学习笔记——字节码指令
JVM学习笔记——字节码指令 字节码 0与 1是计算机仅能识别的信号,经过0和1的不同组合产生了数字之上的操作.另外,通过不同的组合亦产生了各种字符.同样,可以通过不同的组合产生不同的机器指令.在不同 ...
- JVM学习笔记之class文件结构【七】
一.概念 1.1 无符号数: 以 u1.u2.u3.u4.u8 代表 1 个字节,2 个字节.4 个字节.8 个字节的无符号数.无符号数可以描述数字,索引引用.数量值和按照 UTF-8 编码构成的字符 ...
- JVM学习笔记——类加载和字节码技术篇
JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...
- JVM学习笔记:字节码执行引擎
JVM学习笔记:字节码执行引擎 移步大神贴:http://rednaxelafx.iteye.com/blog/492667
- JVM学习笔记-第六章-类文件结构
JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...
- JVM基础系列第5讲:字节码文件结构
温馨提示:此篇文章长达两万字,图片50多张,内容非常多,建议收藏后再看. 前面我们说到 Java 虚拟机使用字节码实现了跨平台的愿景,无论什么系统,我们都可以使用 Java 虚拟机解释执行字节码文件. ...
- <JVM中篇:字节码与类的加载篇>01-Class字节码文件结构
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- JVM学习笔记:虚拟机的类加载机制
JVM类加载机制分两部分来总结: (1)类加载过程 (2)类加载器 一.JVM类加载过程 类的加载过程:加载 →连接(验证 → 准备 → 解析)→ 初始化. 类的生命周期:加载 →连接(验证 → 准备 ...
随机推荐
- hdu3715 Go Deeper[二分+2-SAT]/poj2723 Get Luffy Out[二分+2-SAT]
这题转化一下题意就是给一堆形如$a_i + a_j \ne c\quad (a_i\in [0,1],c\in [0,2])$的限制,问从开头开始最多到哪条限制全是有解的. 那么,首先有可二分性,所以 ...
- C# 委托、lambda表达式和事件 (7) 持续更新
引用方法 在C++,函数指针只不过是一个指向内存位置的指针,它不是类型安全的. C# 委托 定义了返回类型和参数的类型.委托类包含对方法的引用,还可以包含多个方法引用. 定义委托 public del ...
- Eclipse中使用Maven的Jetty插件Debug Web项目
1.环境配置 JAVA_HOME=D:\Program Files\Java\jdk1.7.0_80 JRE_HOME=%JAVA_HOME%\jre CLASSPATH=.;%JAVA_HOME%/ ...
- adreno的tbdr
看完apple的(&&powervr) 再来撸高通的.. FlexRender 能自己选三种模式 Direct Binning 这个只是分块 HWVizBinning 这个估计就是tb ...
- SpringBoot项目中使用Bootstrap 的CSS、JS资源
首先 需要在 application.properties 文件中添加这句 spring.mvc.static-path-pattern=/** 不然是使用不了的 还有一种办法就是 使用bootstr ...
- python json模块小技巧
python的json模块通常用于与序列化数据,如 def get_user_info(user_id): res = {"user_id": 190013234,"ni ...
- react-native-page-listview使用方法(自定义FlatList/ListView下拉刷新,上拉加载更多,方便的实现分页)
react-native-page-listview 对ListView/FlatList的封装,可以很方便的分页加载网络数据,还支持自定义下拉刷新View和上拉加载更多的View.兼容高版本Flat ...
- 解决ubuntu安装软件has install-snap change in progress错误
解决ubuntu安装软件has install-snap change in progress错误 2018年05月06日 13:45:39 山间明月江上清风_ 阅读数:14316 标签: ubunt ...
- C# 父类代码动态转换子类
百度上搜索C# 如何父类运行时转换成子类,没有得到相应答案,突然想起C# 有dynamic类型试试看结果成功了... 以后编写代码类似这样的代码 就可以删减掉了 if (en.type == EMap ...
- tqdm如何在pandas里面使用
原文: https://segmentfault.com/a/1190000016059726 当然,首先我们得载入模块,在notebook中使用tqdm带的基于Js显示的进度条前,请务必检查是否安装 ...