python把桢写入txt_Java 字节码与字节码分析
1.1 Java 字節碼簡介Java 字節碼由單字節(byte)的指令組成,理論上最多支持 256 個操作碼(opcode)。實際上 Java 只使用了200左右的操作碼, 還有一些操作碼則保留給調試操作。
根據指令的性質,主要分為四個大類:1.棧操作指令,包括與局部變量交互的指令
2.程序流程控制指令
3.對象操作指令,包括方法調用指令
4.算術運算以及類型轉換指令
字節碼的運行時結構:JVM 基于棧的計算模型每個線程都有一個獨屬于自己的線程棧(JVM Stack),用于存儲棧幀(Stack Frame)。
每一次方法調用,JVM 都會自動創建一個棧幀。每當為 Java 方法分配棧楨時,JVM 需要開辟一塊額外的空間作為操作數棧,來存放計算的操作數以及返回結果。
棧楨的組成部分:1.局部變量表 (Local Variables)
2.操作數棧 (Operand Stack)
3.動態鏈接 (Dynamic Linking)
4.方法返回地址 (Return Address)
下圖所示:
操作碼(指令)由類型前綴類型前綴和操作名稱兩部分組成,例如: iadd 操作碼,i 代表 Integer 類型,add 代表加法操作,
所以 iadd 就是整數類型數據的加法操作。
1.2 Java 字節碼文件結構簡述字節碼文件結構
1.3 字節碼文件分析以下字節碼分析都是基于 HelloByteCode.java 使用 javap 反編譯之后來展開。
(1) 類信息與常量池信息Classfile /D:/lesson01/bytecode/HelloByteCode.class
Last modified Jan 9, 2021; size 1888 bytes
MD5 checksum d0225425f9a99537afc8664fa6125129
Compiled from "HelloByteCode.java"
public class lesson01.bytecode.HelloByteCode
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#60 // java/lang/Object."":()V
#2 = Fieldref #61.#62 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #32 // myMethod
#4 = Methodref #63.#64 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = String #33 // myStaticMethod
#6 = String #65 // sum = %d, division = %d, multiplication = %d, sub = %d%n
#7 = Class #66 // java/lang/Object
#8 = Methodref #67.#68 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#9 = Methodref #63.#69 // java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
#10 = Long 6000000l
#12 = Float 3.14fminor version 與 major version: 字節碼格式版本號,52.0 = JDK8
flags: 訪問權限修飾與屬性ACC_PUBLIC 表示當前類的訪問修飾是 public
ACC_SUPER 是歷史原因, JDK1.0 的BUG修正中引入 ACC_SUPER 標志來修正 invokespecial 指令調用 super 類方法的問題,從 Java 1.1 開始, 編譯器一般都會自動生成 ACC_SUPER 標志。
Constant pool: 常量池#1、#2: 這些表示常量池成員的編號,常量池成員可以存儲字符串、整數、浮點數、符號引用、常量編號(索引值)
Methodref、Fieldref、String等: 用于說明此常量位存儲的是什么類型的數據,例如 Methodref 代表這個常量指向的是一個方法
(2) 方法信息public double myPublicMethod(int, double, java.lang.String);
descriptor: (IDLjava/lang/String;)D
flags: ACC_PUBLIC
Code:
stack=4, locals=5, args_size=4
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."":()V
10: ldc #5 // String myPublicMethod, x =
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: iload_1
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
19: ldc #8 // String , d =
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: dload_2
25: invokevirtual #9 // Method java/lang/StringBuilder.append:(D)Ljava/lang/StringBuilder;
28: ldc #10 // String , str =
30: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: aload 4
35: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
41: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: iload_1
45: i2d
46: dload_2
47: dadd
48: dreturn
LineNumberTable:
line 12: 0
line 13: 44
LocalVariableTable:
Start Length Slot Name Signature
0 49 0 this Llesson01/bytecode/HelloByteCode;
0 49 1 x I
0 49 2 d D
0 49 4 str Ljava/lang/String;descriptor: 方法描述(IDLjava/lang/String;)D小括號內的為形參參數類型描述,小括號右邊的是方法返回值類型描述。
小括號內 I 代表第一個參數為 int 類型,D 代表第二個參數為 double 類型。L 代表第三個參數是對象類型,java/lang/String 代表是 String 類型對象。
小括號右邊的 D 代表方法的返回值為 double 類型。
flags: 訪問權限修飾符,ACC_PUBLIC 代表 public 方法,ACC_STATIC 代表靜態方法。
Code: 源代碼對應的 JVM 操作碼和操作數區域。在進行字節碼增強時重點操作的就是 Code 區這一部分。操作碼左邊的數字代表當前操作碼在字節碼二進制文件中的字節位置
stack: 執行該方法時需要的棧深度
locals: 需要在局部變量表中保留多少個槽位
args_size: 方法的參數個數此示例有3個形參,但是 args_size 是 4,這是因為非靜態方法有 this 引用,this 被分配在局部變量表的第0號槽位中
LineNumberTable: 行號表將 Code 區的操作碼和源代碼中的行號對應,Debug 的時候可以通過行號表來看源代碼執行一行時需要執行多少個 JVM 操作碼
例如 line 12: 0 ,12代表源代碼的行號,0代表 Code 區操作碼的行號
LocalVariableTable: 局部變量表其中包含了方法的參數,以及在方法體內定義的局部變量。
元素個數等于 args_size
Start 和 Length: 代表當前局部變量或方法參數在 Code 操作碼區中的作用域范圍例如當前示例 start 為0,length 為49代表該變量的作用域從0一直到48,也就是整個方法體。
Slot: 槽位,從0開始,如果訪問的是64位數據類型變量(例如: long類型變量,double類型變量)會占用2個槽位。
Name: 變量名
Signature: 變量類型描述
(3) 基本數據類型變量定義Constant pool:
#22 = Long 6000000l
#24 = Float 3.14f
#25 = Double 4125.5647d
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=15, args_size=1
0: bipush 10
2: istore_1
3: bipush 50
5: istore_2
.... 其他省略
72: iconst_5
73: istore 7
75: ldc2_w #22 // long 6000000l
78: lstore 8
80: ldc #24 // float 3.14f
82: fstore 10
84: ldc2_w #25 // double 4125.5647d
87: dstore 11
89: iconst_1
90: istore 13
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
162 18 14 i I
0 247 0 args [Ljava/lang/String;
3 244 1 x I
6 241 2 y I
13 234 3 sum I
18 229 4 division I
23 224 5 multiplication I
28 219 6 sub I
75 172 7 b B
80 167 8 l J
84 163 10 f F
89 158 11 d D
92 155 13 bool Z
233 14 14 helloByteCode Llesson01/bytecode/HelloByteCode;0行: 將常量10壓入操作數棧的棧頂中
2行: 將棧頂 int 類型的值保存到槽位為1的局部變量中,也就是把常量10賦值給變量 x
3行: 將常量50壓入操作數棧的棧頂中
5行: 將棧頂 int 類型的值保存到槽位為2的局部變量中,也就是把常量10賦值給變量 y
72行: 將常量5壓入操作數棧的棧頂中
73行: 將棧頂 int 類型的值保存到槽位為7的局部變量中,也就是把常量5賦值給變量 b
75行: 將常量編號為22的 long 類型的常量值壓入操作數棧的棧頂中
78行: 將棧頂 long 類型的值保存到槽位為8的局部變量中,也就是把常量6000000賦值給變量 l
80行: 將常量編號為24的 float 類型常量值壓入操作數棧的棧頂中
82行: 將棧頂 float 類型的值保存到槽位為10的局部變量中
84行: 將常量編號為25的 double 類型常量值壓入操作數棧的棧頂中
87行: 將棧頂 double 類型的值保存到槽位為11的局部變量中
(4) 創建對象與對象初始化Constant pool:
#36 = Class #121 // lesson01/bytecode/HelloByteCode
#37 = Methodref #36.#86 // lesson01/bytecode/HelloByteCode."":()V
#41 = Utf8
#42 = Utf8 ()V
#86 = NameAndType #41:#42 // "":()V
#121 = Utf8 lesson01/bytecode/HelloByteCode
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=15, args_size=1
.... 其他省略
224: new #36 // class lesson01/bytecode/HelloByteCode
227: dup
228: invokespecial #37 // Method "":()V
231: astore 14
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
162 18 14 i I
0 247 0 args [Ljava/lang/String;
3 244 1 x I
6 241 2 y I
13 234 3 sum I
18 229 4 division I
23 224 5 multiplication I
28 219 6 sub I
75 172 7 b B
80 167 8 l J
84 163 10 f F
89 158 11 d D
92 155 13 bool Z
233 14 14 helloByteCode Llesson01/bytecode/HelloByteCode;224行: 創建一個 HelloByteCode 對象, 并將其引用的引用值壓入操作數棧的棧頂中,此時沒有調用構造函數,所以對象沒有初始化。
227行: 復制棧頂的 對象引用值 并將復制值壓入操作數棧的棧頂中
228行: 調用 HelloByteCode 的無參構造函數
231行: 將棧頂的引用類型數值保存到槽位為14的局部變量中
為什么創建對象時需要 dup 指令?使用 invokespecial 命令會從操作數堆棧中彈出 nargs 參數值和 objectref ,正是因為需要調用這個函數才導致中間必須要有一個 dup 指令,不然調用完 方法以后,操作數棧為空,就再也找不回剛剛創建的對象了。
下圖所示:
(5) 算術運算public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=15, args_size=1
.... 其他省略
6: iload_1
7: iload_2
8: iadd
9: bipush 60
11: iadd
12: istore_3
13: iload_2
14: iload_1
15: idiv
16: istore 4
18: iload_1
19: iload_2
20: imul
21: istore 5
23: iload_2
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
162 18 14 i I
0 247 0 args [Ljava/lang/String;
3 244 1 x I
6 241 2 y I
13 234 3 sum I
18 229 4 division I
23 224 5 multiplication I
28 219 6 sub I
75 172 7 b B
80 167 8 l J
84 163 10 f F
89 158 11 d D
92 155 13 bool Z
233 14 14 helloByteCode Llesson01/bytecode/HelloByteCode;6行: 將局部變量表中槽位為1的變量值壓入操作數棧的棧頂中
7行: 將局部變量表中槽位為2的變量值壓入操作數棧的棧頂中
8行: 將棧頂兩個 int 型數值相加并將結果壓入棧頂
9行: 將常量60壓入棧頂
11行: 將棧頂兩個 int 型數值相加并將結果壓入棧頂,此時棧頂的值就是 x + y + 60
12行: 將棧頂的值保存到槽位為3的局部變量中
其他算術操作類似,以此類推。
(6) 數組創建與元素訪問public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=18, args_size=1
.... 其他省略
145: iconst_5
146: newarray int
148: dup
149: iconst_0
150: iconst_1
151: iastore
152: dup
153: iconst_1
154: bipush 7
156: iastore
157: dup
158: iconst_2
159: sipush 220
162: iastore
163: dup
164: iconst_3
165: bipush 50
167: iastore
168: dup
169: iconst_4
170: sipush 1000
173: iastore
174: astore 14
176: aload 14
178: iconst_0
179: iaload
180: istore 15
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
244 26 16 i I
249 21 17 len I
0 335 0 args [Ljava/lang/String;
3 332 1 x I
6 329 2 y I
13 322 3 sum I
18 317 4 division I
23 312 5 multiplication I
28 307 6 sub I
75 260 7 b B
80 255 8 l J
84 251 10 f F
89 246 11 d D
92 243 13 bool Z
176 159 14 iArr [I
182 153 15 elementValue I
321 14 16 helloByteCode Llesson01/bytecode/HelloByteCode;145行: 將 int 類型5壓入棧頂
146行: 先從棧彈出獲取棧頂的值,獲取到5,然后創建5個 int 類型元素的數組對象,并把該數組對象的引用值壓入棧頂
148行: 復制棧頂的 數組對象引用值 并將復制值壓入棧頂
149行: 將 int 類型0壓入棧頂,該值為數組下標
150行: 將 int 類型1壓入棧頂,該值為要存入數組的元素值
151行: 從操作數棧彈出三個值,分別是要存入的元素值、數組下標、數組的對象引用,并將元素值存入數組對應的下標中
152行: 因為 iastore 指令會彈出數組的引用,所以要復制一下數組的引用并壓入棧頂,供后續數組操作使用
153行 - 173行: 與上述操作邏輯相同,將元素值存入數組中,此處省略詳細分析
174行: 將棧頂的 數組對象引用 保存到槽位為14的局部變量中,也就是將數組引用賦值給局部變量 iArr
176行: 將槽位為14的局部變量引用值壓入棧頂,也就是將 iArr 數組引用壓入棧頂
178行: 將 int 類型0壓入棧頂,此值為數組的下標
179行: 從棧頂彈出獲取2個值,數組下標值和數組對象引用,在從數組中獲取到該下標的元素值并壓入棧頂
180行: 將棧頂 int 類型數組元素值保存到槽位為15的局部變量中,也就是將數組下標為0的元素值賦值給局部變量 elementValue
(7) 流程控制指令
if else 語句:Constant pool:
#2 = Fieldref #94.#95 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Class #96 // java/lang/StringBuilder
#4 = Methodref #3.#93 // java/lang/StringBuilder."":()V
#6 = Methodref #3.#98 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = Methodref #3.#99 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#11 = Methodref #3.#103 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#12 = Methodref #104.#105 // java/io/PrintStream.println:(Ljava/lang/String;)V
#33 = String #125 // x = 10
#34 = String #126 // x = 20
#35 = String #127 // x =
#43 = Utf8
#44 = Utf8 ()V
#93 = NameAndType #43:#44 // "":()V
#94 = Class #132 // java/lang/System
#95 = NameAndType #133:#134 // out:Ljava/io/PrintStream;
#96 = Utf8 java/lang/StringBuilder
#98 = NameAndType #135:#136 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#99 = NameAndType #135:#137 // append:(I)Ljava/lang/StringBuilder;
#103 = NameAndType #139:#140 // toString:()Ljava/lang/String;
#104 = Class #141 // java/io/PrintStream
#105 = NameAndType #142:#143 // println:(Ljava/lang/String;)V
#125 = Utf8 x = 10
#126 = Utf8 x = 20
#127 = Utf8 x =
#132 = Utf8 java/lang/System
#133 = Utf8 out
#134 = Utf8 Ljava/io/PrintStream;
#135 = Utf8 append
#136 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#137 = Utf8 (I)Ljava/lang/StringBuilder;
#138 = Utf8 (D)Ljava/lang/StringBuilder;
#139 = Utf8 toString
#140 = Utf8 ()Ljava/lang/String;
#141 = Utf8 java/io/PrintStream
#142 = Utf8 println
#143 = Utf8 (Ljava/lang/String;)V
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=18, args_size=1
.... 其他省略
182: iload_1
183: bipush 10
185: if_icmpne 199
188: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
191: ldc #33 // String x = 10
193: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
196: goto 275
199: iload_1
200: bipush 20
202: if_icmple 216
205: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
208: ldc #34 // String x > 20
210: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
213: goto 275
216: iload_1
217: bipush 50
219: if_icmpge 233
222: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
225: ldc #35 // String x < 50
227: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
230: goto 275
233: iload_1
234: bipush 60
236: if_icmplt 250
239: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
242: ldc #36 // String x >= 60
244: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
247: goto 275
250: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
253: new #3 // class java/lang/StringBuilder
256: dup
257: invokespecial #4 // Method java/lang/StringBuilder."":()V
260: ldc #37 // String x =
262: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
265: iload_1
266: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
269: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
272: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
278 26 16 i I
283 21 17 len I
0 371 0 args [Ljava/lang/String;
3 368 1 x I
6 365 2 y I
13 358 3 sum I
18 353 4 division I
23 348 5 multiplication I
28 343 6 sub I
75 296 7 b B
80 291 8 l J
84 287 10 f F
89 282 11 d D
92 279 13 bool Z
176 195 14 iArr [I
182 189 15 elementValue I
357 14 16 helloByteCode Llesson01/bytecode/HelloByteCode;182行: 將槽位為1的局部變量值壓入棧頂
183行: 將常量值10壓入棧頂
185行: 從棧頂彈出2個 int 類型值然后比較,如果結果不相等,就跳轉到第199行,否則就繼續往下執行
188行: 獲取 System 的靜態字段 out 并將其值加入棧頂中
191行: 把字符串常量 x = 10 壓入棧頂
193行: 從棧頂彈出2個值,第一個值是 x = 10 字符串常量,第二個值是 out 靜態字段,然后調用 println 方法打印 x = 10
196行: 已經執行完 if else 代碼塊,所以跳轉到275行,跳出 if else 代碼塊
199行 - 272行: 執行邏輯相同,只是 if 的判斷指令有所不同。
250行 - 272行: 是使用 StringBuilder 進行字符串常量 "x = " 與 局部變量x進行拼接,然后調用 System 的靜態字段 out 的 println 方法打印拼接后的字符串
for 語句:public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=18, args_size=1
.... 其他省略
275: iconst_0
276: istore 16
278: aload 14
280: arraylength
281: istore 17
283: iload 16
285: iload 17
287: if_icmpge 304
290: iload_3
291: aload 14
293: iload 16
295: iaload
296: iadd
297: istore_3
298: iinc 16, 1
301: goto 283
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
278 26 16 i I
283 21 17 len I
0 371 0 args [Ljava/lang/String;
3 368 1 x I
6 365 2 y I
13 358 3 sum I
18 353 4 division I
23 348 5 multiplication I
28 343 6 sub I
75 296 7 b B
80 291 8 l J
84 287 10 f F
89 282 11 d D
92 279 13 bool Z
176 195 14 iArr [I
182 189 15 elementValue I
357 14 16 helloByteCode Llesson01/bytecode/HelloByteCode;275行: 將 int 類型0壓入棧頂
276行: 將棧頂值保存到槽位為16的局部變量中,此時槽位為16的局部變量是 i
278行: 將槽位為14的引用變量值壓入棧頂,就是 iArr 數組對象
280行: 首先彈出棧頂的數組對象引用值,獲取該數組的長度值并壓入棧頂
281行: 將棧頂值保存到槽位為17的局部變量中,就是把數組長度保存到局部變量 len
283行: 將槽位為16的局部變量值壓入棧頂,局部變量 i 的值
285行: 將槽位為17的局部變量值壓入棧頂,局部變量 len 的值
287行: 先從棧中彈出兩個操作數,局部變量 len 的值和局部變量 i 的值,比較 len 和 i,當 len >= i 時則跳轉到304行,跳出循環
290行 - 301行: 為 for 循環體相關字節碼
290行: 將槽位為3的局部變量值壓入棧頂,局部變量 sum 的值
291行: 將槽位為14的局部變量引用值壓入棧頂,也就是 iArr 數組對象引用值
293行: 將槽位為16的局部變量 int 類型值壓入棧頂,也就是局部變量 i 的值
295行: 先從棧頂彈出兩個值,第一個是局部變量 i 的值,第二個是 iArr 數組對象引用值,將 i 的值作為數組下標,取出對應的元素值并壓入棧頂
296行: 先從棧頂彈出兩個值,第一個是 iArr[i] 的值,第二個是局部變量 sum 的值,并將它們相加,然后把相加后的值壓入棧頂
297行: 將棧頂的 int 類型值保存到槽位為3的局部變量中,局部變量 sum 中
298行: 將槽位為16的局部變量值自增1,也就是 i++
301行: 跳轉到283行,重新判斷循環條件,滿足就繼續執行循環體,否則跳出循環
switch 語句:public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=18, args_size=1
.... 其他省略
304: iload_2
305: lookupswitch { // 2
10: 332
50: 340
default: 348
}
332: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
335: ldc #38 // String y = 10
337: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
340: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
343: ldc #39 // String y = 50
345: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
348: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
351: ldc #40 // String default
353: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
.... 其他省略
246: return
LocalVariableTable:
Start Length Slot Name Signature
278 26 16 i I
283 21 17 len I
0 379 0 args [Ljava/lang/String;
3 376 1 x I
6 373 2 y I
13 366 3 sum I
18 361 4 division I
23 356 5 multiplication I
28 351 6 sub I
75 304 7 b B
80 299 8 l J
84 295 10 f F
89 290 11 d D
92 287 13 bool Z
176 203 14 iArr [I
182 197 15 elementValue I
365 14 16 helloByteCode Llesson01/bytecode/HelloByteCode;304行: 將槽位為2的局部變量值壓入棧頂,局部變量 y 的值壓入棧頂
305行: 從棧頂彈出獲取到局部變量 y 的值,并進行值的匹配,y 的值為10時跳轉到332行,y 的值為50時跳轉到340行,如果都不匹配執行 default 分支跳轉到348行
332行 - 353行: 都是調用不同匹配值的分支,調用 println 打印不同的字符串
(8) 方法調用指令和參數傳遞Constant pool:
#14 = String #109 // Hello
#41 = Class #136 // lesson01/bytecode/HelloByteCode
#42 = Methodref #41.#96 // lesson01/bytecode/HelloByteCode."":()V
#43 = Double 90.0d
#45 = Methodref #41.#137 // lesson01/bytecode/HelloByteCode.myPublicMethod:(IDLjava/lang/String;)D
#46 = Utf8
#47 = Utf8 ()V
#53 = Utf8 myPublicMethod
#54 = Utf8 (IDLjava/lang/String;)D
#96 = NameAndType #46:#47 // "":()V
#109 = Utf8 Hello
#136 = Utf8 lesson01/bytecode/HelloByteCode
#137 = NameAndType #53:#54 // myPublicMethod:(IDLjava/lang/String;)D
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=18, args_size=1
.... 其他省略
356: new #41 // class lesson01/bytecode/HelloByteCode
359: dup
360: invokespecial #42 // Method "":()V
363: astore 16
365: aload 16
367: bipush 80
369: ldc2_w #43 // double 90.0d
372: ldc #14 // String Hello
374: invokevirtual #45 // Method myPublicMethod:(IDLjava/lang/String;)D
377: pop2
378: return
LocalVariableTable:
Start Length Slot Name Signature
278 26 16 i I
283 21 17 len I
0 379 0 args [Ljava/lang/String;
3 376 1 x I
6 373 2 y I
13 366 3 sum I
18 361 4 division I
23 356 5 multiplication I
28 351 6 sub I
75 304 7 b B
80 299 8 l J
84 295 10 f F
89 290 11 d D
92 287 13 bool Z
176 203 14 iArr [I
182 197 15 elementValue I
365 14 16 helloByteCode Llesson01/bytecode/HelloByteCode;356行: 創建 HelloByteCode 對象,并將對象引用值壓入棧頂
359行: 復制棧頂的對象引用值并壓入棧頂
360行: 調用 HelloByteCode 的無參構造函數
363行: 將棧頂的 HelloByteCode 對象引用值保存到槽位為16的局部變量中,此時的局部變量已經是 helloByteCode ,之前同樣槽位為16的局部變量 i 超過了作用域,復用了同一個槽位
365行: 將槽位為16的 helloByteCode 局部變量引用值壓入棧頂
367行: 將常量值80壓入棧頂
369行: 將常量編號為43的浮點型常量值90.0壓入棧頂
372行: 將字符串常量值 Hello 壓入棧頂
374行: 彈出棧中所有數據調用 HelloByteCode 的 myPublicMethod 方法并將參數傳入,執行完后將返回值壓入棧頂
377行: 從棧頂將返回值彈出
378行: 方法返回
上述的情況是 myPublicMethod 方法有返回值,但是沒有局部變量去接收的情況。
myPublicMethod 方法沒有返回值的字節碼情況:374: invokevirtual #45 // Method myPublicMethod:(IDLjava/lang/String;)V
377: return沒有返回值會直接 return
myPublicMethod 方法有返回值并且也有局部變量接收的字節碼情況:374: invokevirtual #45 // Method myPublicMethod:(IDLjava/lang/String;)D
377: dstore 17
379: return有返回值且有局部變量接收,會使用指令將在棧頂的返回值保存到對應的局部變量
1.4 字節碼相關 JDK 命令行工具javac 編譯工具
常用參數:-g 在生成的class文件中包含所有調試信息(包括局部變量),缺省情況下只生成行號和源文件信息。
-g:none 不生成任何調試信息
-g:{關鍵字列表} 只生成某些類型的調試信息,這些類型由逗號分隔的關鍵字列表所指定
-encoding 指定編碼
-verbose 冗長輸出。它包括了每個所加載的類和每個所編譯的源文件的有關信息。
示例: javac -encoding utf-8 -g HelloByteCode.java
javap 查看字節碼工具
常用參數:-c 輸出分解后的代碼,例如,類中每一個方法內,包含 java 字節碼的指令
-verbose 輸出棧大小,方法參數的個數、局部變量表等
示例: javap -c -verbose HelloByteCode.class
總結
以上是生活随笔為你收集整理的python把桢写入txt_Java 字节码与字节码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql防注入原理_MyBatis如何
- 下一篇: vba 跳出for循环_VBA简单入门0