上一篇笔记的内容大部分没有实际动手操作,因此决定完成这个完整的练习并记录下来。

另注,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打印出来的信息可以验证,确实是这样的

接下来是具体的常量分析,所以将常量池中的数据类型结构图,和相关知识要点贴出来

要点:

  1. 使用java -verbose 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息
  2. 魔数:所有.class字节码文件的前四个字节都是魔数,,魔数值为固定值:0xCAFEBABE
  3. 魔数之后的4个字节代码版本号,前2个字节代表次版本号(minor version),后2个字节代表主版本号(major version),以上图为例,次版本号为0,主版本号为52,所以,该文件的版本号是1.8.0
  4. 常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个Java类定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法与变量信息,都是存储在常量池中。常量池中主要存储两类常量:字面量与符号应用。字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等
  5. 常量池的总体结构:Java类所对应的常量池主要由“常量池数量”与“常量池数组”这两部分共同构成。常量池数量紧跟在主版本号后面(也就是第9个字节开始),占据2个字节;常量池数组则紧跟在常量池数量之后。常量池数组与一般的数组不同的是,常量池数组中不同的原素的类型、结构都是不同的,长度当然也就不同,但是,每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。以上图为例,第9到第10个字节连起来时 0018,即十进制数字的24,意即后面有24个常量。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池”的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量池数组(也叫常量表)中,这个常量就对应null值,所以常量池的索引从1而非0开始。以上图为例显示有24个常量,但是根据 java -verbose的输出结果,显示只有23个常量。
  6. 在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;
  7. 对于数组类型来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I,String[][]被记录为[[Ljava/lang/String;
  8. 用描述符来描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之类,如方法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:复制操作数堆栈上的顶部值,并将复制 的值压入操作数堆栈。

先解释到这里吧,感觉还需要学习。

最新文章

  1. maven安装和配置
  2. 【菜鸟玩Linux开发】在C++里操作MySQL
  3. Css学习笔记 (一)
  4. Hbase之取出行数据指定部分+版本控制(类似MySQL的Limit)
  5. [实变函数]3.2 可测集 (measurable set)
  6. springMVC数据封装成POJO
  7. SRM470 - SRM474(1-250pt,500pt)(471-500pt为最短路,474-500pt未做)
  8. NET基础课--Linq第三讲
  9. sgu176 Flow Construction【有源汇有上下界最小流】
  10. day20包
  11. java学习之—链表(3)
  12. python操作execl学习笔记(一)
  13. Java远程连接redis, 报错 Connection refused: connect
  14. 计算概论(A)/基础编程练习(数据成分)/2:奥运奖牌计数
  15. .Net Excel操作之NPOI(二)常用操作封装
  16. python-day17--生成器
  17. ABAP-金额小写转大写
  18. Session和Cookie之间区别与联系
  19. Nginx完整配置配置样例
  20. Python 爬虫 学习一

热门文章

  1. kotlin字符串模板&amp;条件控制
  2. HDU 4085 斯坦纳树+DP
  3. Kummer定理
  4. 前端知识体系:JavaScript基础-一个原型继承例子
  5. [BZOJ 1146] [CTSC2008]网络管理Network(树状数组+主席树)
  6. C# Socket 编程 Sample
  7. Java的23种设计模式 &lt;二&gt;
  8. [Vue] : 键盘修饰符
  9. Selenium常见异常分析及解决方案
  10. Linux网络编程六、报文解析(1)