成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一篇帶給你JVM 字節碼解析過程

開發 后端
我們可以使用 javap -verbose 命令分析一個字節碼文件時, 將會分析該字節碼文件的魔數、版本號、常量池、類信息、類的構造方法、類中的方法信息、類變量與成員變量等信息。

概述

概述本文主要是基于 .class 文件,進行分析 .class 文件的內容。

這部分個人覺得主要是屬于設計機構拓展的內容,大家可以一起來學習一下 Java 字節碼的設計結構以及感受一下設計者的設計。

class 類文件結構

Java 提供 javap 命令可以分析字節碼文件,我們可以使用 javap -verbose 命令分析一個字節碼文件時, 將會分析該字節碼文件的魔數、版本號、常量池、類信息、類的構造方法、類中的方法信息、類變量與成員變量等信息。

一個簡單的 Java 代碼

  1. public class TestClass { 
  2.  
  3.     private int m; 
  4.  
  5.     public int inc() { 
  6.        return ++m; 
  7.     } 

下圖顯示的是 Java 代碼編譯后 .class 文件的十六進制信息

bytecode_十六進制.png

為了方便對比我執行一下 javap -v TestClass

  1. Classfile /../../TestClass.class 
  2.   Last modified 2021-2-6; size 306 bytes 
  3.   MD5 checksum eeba40cc40cc28ef4d416ff70d901561 
  4.   Compiled from "TestClass.java" 
  5. public class cn.edu.cqvie.jvm.bytecode.TestClass 
  6.   minor version: 0 
  7.   major version: 52 
  8.   flags: ACC_PUBLIC, ACC_SUPER 
  9. Constant pool: 
  10.    #1 = Methodref          #4.#15         // java/lang/Object."<init>":()V 
  11.    #2 = Fieldref           #3.#16         // cn/edu/cqvie/jvm/bytecode/TestClass.m:I 
  12.    #3 = Class              #17            // cn/edu/cqvie/jvm/bytecode/TestClass 
  13.    #4 = Class              #18            // java/lang/Object 
  14.    #5 = Utf8               m 
  15.    #6 = Utf8               I 
  16.    #7 = Utf8               <init> 
  17.    #8 = Utf8               ()V 
  18.    #9 = Utf8               Code 
  19.   #10 = Utf8               LineNumberTable 
  20.   #11 = Utf8               inc 
  21.   #12 = Utf8               ()I 
  22.   #13 = Utf8               SourceFile 
  23.   #14 = Utf8               TestClass.java 
  24.   #15 = NameAndType        #7:#8          // "<init>":()V 
  25.   #16 = NameAndType        #5:#6          // m:I 
  26.   #17 = Utf8               cn/edu/cqvie/jvm/bytecode/TestClass 
  27.   #18 = Utf8               java/lang/Object 
  28.   public cn.edu.cqvie.jvm.bytecode.TestClass(); 
  29.     descriptor: ()V 
  30.     flags: ACC_PUBLIC 
  31.     Code: 
  32.       stack=1, locals=1, args_size=1 
  33.          0: aload_0 
  34.          1: invokespecial #1                  // Method java/lang/Object."<init>":()V 
  35.          4: return 
  36.       LineNumberTable: 
  37.         line 3: 0 
  38.  
  39.   public int inc(); 
  40.     descriptor: ()I 
  41.     flags: ACC_PUBLIC 
  42.     Code: 
  43.       stack=3, locals=1, args_size=1 
  44.          0: aload_0 
  45.          1: dup 
  46.          2: getfield      #2                  // Field m:I 
  47.          5: iconst_1 
  48.          6: iadd 
  49.          7: dup_x1 
  50.          8: putfield      #2                  // Field m:I 
  51.         11: ireturn 
  52.       LineNumberTable: 
  53.         line 8: 0 
  54. SourceFile: "TestClass.java" 

Java 字節碼結構

bytecode_結構.png

1. 魔數和 Class 文件版本

魔數:所有的.class 字節碼文件的前4個字節都是魔數,魔數為固定值: 0xCAFEBABE

版本信息,魔數之后的4個字節是版本信息,前兩個字節表示 minor version (次版本號), 后2個字節表示major version (主版本號)。這里的版本號 00 00 00 34 換算成十進制表, 表示次版本號為0, 主版本號為 52. 所以該文件的版本號為 1.8.0。可以通過 java -version 來驗證這一點。

  1. ➜  ~ java -version 
  2. java version "1.8.0_281" 
  3. Java(TM) SE Runtime Environment (build 1.8.0_281-b09) 
  4. Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode) 

2. 常量池

常量池 (constant pool): 2+N個字節 緊接著主版本號之后的就是常量池入口。一個java 類中定義的很多信息都是由常量池來描述的,可以將常量池看作是 Class 文件的資源倉庫,比如說Java類中變量的方法與變量信息,都是存儲在常量池中。常量池中主要存儲2類常量:字面量與符號引用。

  • 字面量, 如字符串文本,java 中聲明為final 的常量值等。
  • 符號引用, 如類和接口的全局限定名, 字段的名稱和描述符,方法的名稱和描述符等。

常量池的總體結構:Java類所對應的常量池主要由常量池(常量表)的數量與常量池數組這兩部分共同構成。常量池中常量數量緊跟著在主版本號后面,占據2字節: 常量池長度 比如這里我們的十六進制就是 00 13 代表有 18 個常量。常量數組則緊跟著常量池數量之后。常量池數組與一般數組不同的是, 常量池數組中不同的元素的類型,結構都是不同的。長度當然也就不同;但是, 一種元素的第一種元素的第一個數據都是一個u1類型, 該字節是一個標識位,占據1個字節。JVM在解析常量池時,會更具這個u1 類型來獲取元素的具體類型。值得注意的是:常量池中元素的個數 = 常量池數 -1 (其中0暫時不適用), 目的是滿足某些常量池索引值的數據在特定情況下需要表達【不引用任何一個常量池】的含義:根本原因在于,索引為0也是一個常量(保留常量),只不過他不位于常量表中。這個常量就對應null值, 所以常量池的索引是從1開始而非0開始。

上面表中描述了11種數據類型的機構, 其實在jdk1.7之后又增加了3種(CONSTANT_MethodHandle_info, CONSTANT_MethodType_info 以及CONSTANT_InvokeDynami_info)。這樣一共14種。

在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[][]被表示為 [[java/lang/String;

用描述符描述方法時, 按照先參數列表, 后返回值的順序來描述. 參數列表按照參數的嚴格順序放在一組()內, 如方法: String getRealnameByIdNickname(int id, String name)的描述符為: (I, Ljava/lang/String;) Ljava/lang/String

Class 字節碼中有兩種數據類型

  • 字節數據直接量:這是基本的數據類型。共細分為u1、u2、u4、u8四種,分別代表連續的1個字節、2個字節、4個字節、8個字節組成的整體數據。
  • 表(數組):表示由多個基本數據或其他表,按照既定順序組成的大的數據集合。表是有結構的 。它的結構體現在:組成表的成分所在的位置和順序都是 嚴格定義好的。

常量池常量

  1. 0A 00 04 00 0F method_info 00 04 指向常量池中的常量的位置, 00 0F 指向的是 15 的位置。
  2. 09 00 03 00 10 field_info 00 03 指向 3 的位置 00 10 (16 位置)
  3. 07 00 11 class info 00 11 (17 位置)
  4. 07 00 12 class info 00 12 (18 位置)
  5. 01 00 01 6D utf8 長度為 1 內容為 6D 表示內容轉換為 10 進制為 109 轉換為 ASCII 碼最后的結果為 m
  6. 01 00 01 49 utf8 長度為 1 內容為 I
  7. 01 00 06 3C 69 6E 69 74 3E utf8 長度為 8 內容為
  8. 01 00 03 28 29 56 utf8 長度為 3 內容為:()V
  9. 01 00 04 43 6F 64 65 utf8長度為 4 內容為 Code
  10. 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 utf8 長度為 15 內容為 LineNumberTable
  11. 01 00 03 69 6E 63 utf8 長度為3 內容為 inc
  12. 01 00 03 28 29 49 長度為 3 內容為 ()I
  13. 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 長度為 10 內容為 SourceFile
  14. 01 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61 長度為 14 內容為 TestClass.java
  15. 0C 00 07 00 08 NameAndType 內容 指向 7 位置。00 08 (8 位置)
  16. 0C 00 05 00 06 NameAndType 內容 指向 5 位置。00 06 (6 位置)
  17. 01 00 23 63 6E 2F 65 64 75 2F 63 71 76 69 65 2F 6A 76 6D 2F 62 79 74 65 63 6F 64 65 2F 54 65 73 74 43 6C 61 73 73 長度 35 內容 cn/edu/cqvie/jvm/bytecode/TestClass
  18. 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 長度 16 內容 java/lang/Object

3. 訪問標志

Access_Flag 訪問標志 訪問標識信息包括該Class文件時類和接口是否被定義成了public,是否是 abstract, 如果是類,是否被申明為成final。通過扇面的源代碼。

0x 00 21: 表示是0x0020 和0x0001的并集, 表示 ACC_PUBLIC 與 ACC_SUPER

bytecode_訪問標志.png

4. 類索引、父類索引

00 03 類名, 03 常量池位置 cn/edu/cqvie/jvm/bytecode/TestClass

00 04 父類名. 04 常量池位置 java/lang/Object

00 00 接口個數, 0 個, 接口個數最多 65535

5. 字段表集合

00 01 字段的個數, 這里有一個

bytecode_字段表.png

字段表結構

  1. field_info { 
  2.   u2 access_flags; 00 02 Fieldref 
  3.   u2 name_index; 00 05 (表示字段名稱在常量池中的索引位置) m 
  4.   u2 descriptor_index; 00 06 (描述符索引) I  
  5.   u2 attributes_count; 00 00 
  6.   attribute_info attributes[attributes_count]  

6. 方法表集合

00 02 表示有兩個方法

bytecode_方法表.png

方法表結構

  1. method_info { 
  2.   u2 access_flags; 00 01  Methodref 
  3.   u2 name_index;  // 00 07  <init> 
  4.   u2 descriptor_index; 00 08 // ()V 
  5.   u2 attributes_count; 00 01 // 屬性結構 
  6.   attributes_info attributes[attributes_count] 

方法屬性結構

  1. attribute_info { 
  2.   u2 attribute_name_index; 00 09 // Code 
  3.   u4 attribute_length; 00 00 00 1D 長度 29 
  4.   u1 info[attribute_length];   
  5.   ...  

7. 屬性表集合

00 09 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 00

"Code" 表示下面是執行代碼

JVM 預定義了一部分的attribute, 但是編譯器自己也可以實現自己的attribute 寫入class文件中, 供運行時使用。不同的attribute 通過attribute_name_index 來區分。

JVM 規范預定義的attribute

Code 結構

Code attribute 的作用是保存該放的的結構,如所對應的字節碼

  1. Code_attribute { 
  2.     u2 attribute_name_index;  // 00 09 ==> Code 
  3.     u4 attribute_length; // 00 00 00 1D ==> 29 
  4.     u2 max_stack;   //  00 01 棧深度為 1 (棧幀中操作數棧的深度) 
  5.     u4 code_length;   // 00 00 00 05 指令的長度是多少 
  6.     u1 code[code_lenght]; // 2A B7 00 01 B1 其中 2A 
  7.      
  8.     // 2A aload_0 
  9.     // B7 invokespecial 
  10.     // 00 #1 
  11.     // 01 <java/lang/Object.<init>> 
  12.     // B1 return 
  13.      
  14.     // 0 aload_0 
  15.   // 1 invokespecial #1 <java/lang/Object.<init>> 
  16.   // 4 return 
  17.     u2 exception_table_length; // 00 00  
  18.     { 
  19.         u2 start_pc; 
  20.         u2 end_pc; 
  21.         u2 handler_pc; 
  22.         u2 catch_type; 
  23.     } exception_table[exception_table_lenght]; 
  24.     u2 attributes_count; // 00 01 
  25.     attribute_info attributes[attributes_count]; 

attribute_length 表示 attribute 所包含的字節數, 不包含attribute_name_index 和 attribute_length 字段。

max_stack 表示這個方法運行的任何時刻所能達到的操作數棧的最大深度。

max_locals 表示方法執行期間創建的局部變量的數目,包含用來表示傳入的參數的局部變量。

code_length 表示該方法所包含的字節碼的字節數以及具體的指令碼。

具體字節碼即是該方法被調用時,虛擬機所執行的字節碼。

exception_table, 這里存放的是處理異常信息。

每個 exception_table, 這里存放的是處理異常的信息。

  • 每個 exception_table 表項由start_pc , end_pc, handler_pc, catch_type 組成。
  • start_pc 和 end_pc 表示在code數組中的從 start_pc 到 end_pc 處(包含start_pc, 不包含end_pc)的指令拋出的異常會由 這個表項來處理。
  • handler_pc 表示處理異常的代碼的開始處。catch_type 表示會被處理的異常類型, 它指向常量池里的一個異常類。當catch_type為0時, 表示處理所有的異常。
  • LineNumberTable 的結構
  1. LineNumberTable_attribute { 
  2.     u2 attribute_name_index; //00 0A 常量池10號位置  LineNumberTable 
  3.     u4 attribute_lenght;  // 00 00 00 06 一共 6個長度 
  4.     u2 line_number_table_length;  // 00 01 映射的對數  1 對 
  5.     line_number_info { 
  6.         u2 start_pc; // 指令行號  00 00  
  7.         u2 line_number; // 源碼調試  00 03 
  8.     } 
  9.     line_number_table[line_number_table_length];  

局部變量表 LocalVariableTable

  1. LocalVariableTable_attribute { 
  2.     u2 attribute_name_index;  
  3.     u4 attribute_lenght; 
  4.     u2 local_variable_table_length;  
  5.     { 
  6.         u2 start_pc; 
  7.         u2 line_number; 
  8.         u2 name_index; 
  9.         u2 descriptor_index;  
  10.         u2 index
  11.     } 

總結

  1. 構造方法中會初始化成員屬性的默認值,如果自己實現了默認的構造方法, 依然還是在構造方法中賦值,這就是對指令的重排序。
  2. 如果多個構造方法那么每個構造方法中都有初始化成員變量的屬性,來保障每個構造方法初始化的時候都會執行到屬性的初始化過程。
  3. 如果構造方法中有執行語句, 那么會先執行賦值信息, 然后在執行自定義的執行代碼。
  4. 對于Java每一個實例方法(非靜態方法), 其在編譯后生成的字節碼中比實際方法多一個參數, 它位于方法的第一個參數位置. 我們就可以在當前方法中 的this去訪問當前對象中的this這個操作是在Javac 編譯器在編譯期間將this的訪問轉換為對普通實例方法的參數訪問,接下來在運行期間, 由JVM的調用實例方法時, 自動向實例方法中傳入該this參數, 所以在實例方法的局部變量表中, 至少會一個指向當前對象的局部變量。
  5. 字節碼對于處理異常的方式:
  • 統一采用異常表的方式來對異常處理。
  • 在Jdk1.4.2之前的版本中, 并不是使用異常表的方式來對異常進行處理的,而是采用特定的指令方式。
  • 當異常處理存在finally 語句塊時,現代化的JVM才去的處理方式將finally語句塊的自己拼接到每一個catch塊后面, 換句話說,程序中中存在多個catch塊,就會在每一個catch塊后面重復多少個finally 的語句塊字節碼。

 

責任編輯:姜華 來源: 運維開發故事
相關推薦

2022-01-14 11:45:40

JVM 虛擬機Java

2023-02-27 10:17:05

EventBus觀察者模式

2023-04-09 21:39:48

JavaScript開源

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2021-06-21 14:36:46

Vite 前端工程化工具

2021-01-28 08:55:48

Elasticsear數據庫數據存儲

2023-03-29 07:45:58

VS編輯區編程工具

2021-04-14 14:16:58

HttpHttp協議網絡協議

2021-04-08 11:00:56

CountDownLaJava進階開發

2022-03-22 09:09:17

HookReact前端

2021-07-21 09:48:20

etcd-wal模塊解析數據庫

2021-04-01 10:51:55

MySQL鎖機制數據庫

2021-03-12 09:21:31

MySQL數據庫邏輯架構

2022-02-17 08:53:38

ElasticSea集群部署

2022-04-29 14:38:49

class文件結構分析

2024-06-13 08:34:48

2022-02-25 15:50:05

OpenHarmonToggle組件鴻蒙

2021-10-28 08:51:53

GPIO軟件框架 Linux

2023-03-13 09:31:04

2021-07-08 07:30:13

Webpack 前端Tree shakin
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美性区| 精品久久久久久亚洲综合网 | 亚洲精品乱码久久久久久黑人 | 色婷婷亚洲国产女人的天堂 | 日韩插插 | 久久久久国产精品一区 | 99视频免费播放 | 午夜影院在线观看 | 黑人精品 | 国产伦精品一区二区三区照片91 | 亚洲视频在线看 | 欧美一区二区三区在线免费观看 | 给我免费的视频在线观看 | 久久这里只有 | 一本岛道一二三不卡区 | 粉嫩一区二区三区性色av | 亚洲精品电影在线 | 国产精品美女久久久久久免费 | 一区二区三区国产 | 国产精品99久久久久久人 | 欧美一区二区三区免费在线观看 | 激情久久av一区av二区av三区 | 99国产精品久久久久老师 | 欧美福利专区 | 国产亚洲一区精品 | 欧美一级片在线 | 在线看av的网址 | 国产精品久久久久久久免费大片 | 亚洲国产精品一区二区三区 | 日韩精品一区二区三区在线观看 | 精品视频成人 | 九色综合网| 在线免费观看成年人视频 | 日韩电影一区二区三区 | 一区二区三区小视频 | 国产精品国产馆在线真实露脸 | 本道综合精品 | 免费毛片在线 | 99re视频在线 | 久久久久91 | 亚洲视频不卡 |