【JVM】- 類加載與字節碼結構1

類文件結構

ClassFile {u4             magic;u2             minor_version;u2             major_version;u2             constant_pool_count;cp_info        constant_pool[constant_pool_count-1];u2             access_flags;u2             this_class;u2             super_class;u2             interfaces_count;u2             interfaces[interfaces_count];u2             fields_count;field_info     fields[fields_count];u2             methods_count;method_info    methods[methods_count];u2             attributes_count;attribute_info attributes[attributes_count];
}

1. 魔數(magic)和版本號

  • magic (4字節): 固定值0xCAFEBABE,用于識別類文件格式
  • minor_version (2字節): 次版本號
  • major_version (2字節): 主版本號,對應Java版本
    • Java 8: 52 (0x34)
    • Java 11: 55 (0x37)
    • Java 17: 61 (0x3D)

2. 常量池(constant_pool)

  • constant_pool_count (2字節): 常量池中項的數量加1(從1開始索引)
  • constant_pool[]: 常量池表,包含多種類型的常量:
    • Class信息 (CONSTANT_Class_info)
    • 字段和方法引用 (CONSTANT_Fieldref_info, CONSTANT_Methodref_info)
    • 字符串常量 (CONSTANT_String_info)
    • 數值常量 (CONSTANT_Integer_info, CONSTANT_Float_info等)
    • 名稱和描述符 (CONSTANT_NameAndType_info)
    • 方法句柄和類型 (CONSTANT_MethodHandle_info, CONSTANT_MethodType_info)
    • 動態調用點 (CONSTANT_InvokeDynamic_info)

3. 類訪問標志(access_flags)

表示類或接口的訪問權限和屬性,如:

  • ACC_PUBLIC (0x0001): public類
  • ACC_FINAL (0x0010): final類
  • ACC_SUPER (0x0020): 使用新的invokespecial語義
  • ACC_INTERFACE (0x0200): 接口
  • ACC_ABSTRACT (0x0400): 抽象類
  • ACC_SYNTHETIC (0x1000): 編譯器生成的類
  • ACC_ANNOTATION (0x2000): 注解類型
  • ACC_ENUM (0x4000): 枚舉類型

4. 類和父類信息

  • this_class (2字節): 指向常量池中該類名的索引
  • super_class (2字節): 指向常量池中父類名的索引(接口的super_class是Object)

5. 接口(interfaces)

  • interfaces_count (2字節): 實現的接口數量
  • interfaces[]: 每個元素是指向常量池中接口名的索引

6. 字段(fields)

  • fields_count (2字節): 字段數量
  • field_info[]: 字段表,每個字段包括:
    • 訪問標志(如public, private, static等)
    • 名稱索引(指向常量池)
    • 描述符索引(指向常量池)
    • 屬性表(如ConstantValue, Synthetic等)

7. 方法(methods)

  • methods_count (2字節): 方法數量
  • method_info[]: 方法表,每個方法包括:
    • 訪問標志(如public, synchronized等)
    • 名稱索引(指向常量池)
    • 描述符索引(指向常量池)
    • 屬性表(最重要的Code屬性包含字節碼)

8. 屬性(attributes)

  • attributes_count (2字節): 屬性數量
  • attribute_info[]: 屬性表,可能包含:
    • SourceFile: 源文件名
    • InnerClasses: 內部類列表
    • EnclosingMethod: 用于局部類或匿名類
    • Synthetic: 表示由編譯器生成
    • Signature: 泛型簽名信息
    • RuntimeVisibleAnnotations: 運行時可見注解
    • BootstrapMethods: 用于invokedynamic指令

字節碼執行流程

有一段java代碼如下:

public class Demo02 {public static void main(String[] args) {int a = 10;int b = a++ + ++a + a--;}
}
  1. 常量池載入運行時常量池
  2. 方法字節碼載入方法區
  3. main線程開始運行,分配棧幀內存
    在這里插入圖片描述

1. 變量初始化 a = 10

0: bipush 10      // 將常量10壓入操作數棧
2: istore_1       // 將棧頂值(10)存儲到局部變量1(a)

此時內存狀態:

  • 局部變量表:a = 10
  • 操作數棧:[空]

2. 計算 a++ (第一個操作數)

3: iload_1        // 加載局部變量1(a)的值到棧頂 → 棧:[10]
4: iinc 1, 1      // 局部變量1(a)自增1 (a=11),注意這不會影響棧頂值

此時內存狀態:

  • 局部變量表:a = 11
  • 操作數棧:[10] (a++表達式的值是自增前的值)

3. 計算 ++a (第二個操作數)

7: iinc 1, 1      // 局部變量1(a)先自增1 (a=12)
10: iload_1       // 然后加載a的值到棧頂 → 棧:[10, 12]

此時內存狀態:

  • 局部變量表:a = 12
  • 操作數棧:[10, 12] (++a表達式的值是自增后的值)

4. 第一次加法 a++ + ++a

11: iadd          // 彈出棧頂兩個值相加,結果壓棧 → 10+12=22 → 棧:[22]

5. 計算 a-- (第三個操作數)

12: iload_1       // 加載a的值到棧頂 → 棧:[22, 12]
13: iinc 1, -1    // 局部變量1(a)自減1 (a=11),不影響棧頂值

此時內存狀態:

  • 局部變量表:a = 11
  • 操作數棧:[22, 12] (a–表達式的值是自減前的值)

6. 第二次加法 (前兩個之和) + a--

16: iadd          // 22+12=34 → 棧:[34]

7. 存儲結果到b

17: istore_2      // 將棧頂值(34)存儲到局部變量2(b)

最終內存狀態:

  • 局部變量表:a = 11, b = 34
  • 操作數棧:[空]

完整字節碼序列

0: bipush 10       // a = 10
2: istore_1
3: iload_1         // 開始計算a++
4: iinc 1, 1
7: iinc 1, 1       // 開始計算++a
10: iload_1
11: iadd           // 前兩個相加
12: iload_1        // 開始計算a--
13: iinc 1, -1
16: iadd           // 與第三個相加
17: istore_2       // b = 結果

后置自增/減(i++):

  • 先使用變量的當前值參與運算
  • 執行自增/減操作
  • 字節碼表現為先iload后iinc

前置自增/減(++i):

  • 先執行自增/減操作
  • 使用新值參與運算
  • 字節碼表現為先iinc后iload

案例分析

分析i++

public class Demo02 {public static void main(String[] args) {int i = 0, x = 0;while(i < 10) {x = x++;++i;}System.out.println(x); // 0}
}

由于是x++,所以x先iload進入操作數棧【0】,再執行iinc進行自增【1】。自增后進行復制,又將操作數棧中的x賦值給x【0】,此時操作數棧中x的值為0。一次循環后,x的值還是0;最終x輸出0。

構造方法<cinit>()V

public class Demo03 {static int i = 10;static {i = 20;}static {i = 30;}public static void main(String[] args) {System.out.println(i); // 30}
}

編譯器會按照從上至下的順序, 收集所有的static靜態代碼塊和靜態成員賦值的代碼,合并成一個特殊的方法<cinit>()V

靜態變量和靜態代碼塊按代碼中的書寫順序依次執行,后執行的會覆蓋前邊的賦值。

構造方法<init>()V

public class Demo04 {private String a = "s1";{b = 20;}private int b = 10;{a = "s2";}public Demo04(String a, int b) {this.a = a;this.b = b;}public static void main(String[] args) {Demo04 d = new Demo04("s3", 30);System.out.println(d.a + " " + d.b); // s3 30}
}

編譯器會按照從上往下的順序,收集所有的{}代碼塊和成員變量賦值的代碼,形成新的構造方法,但是原始構造方法內的代碼總是在最后邊。

方法調用

public class Demo05 {private void test1(){}private final void test2(){}public void test3(){}public static void test4(){}public static void main(String[] args) {Demo05 d = new Demo05();d.test1();d.test2();d.test3();d.test4();Demo05.test4();}
}

在這里插入圖片描述

每次調用方法的時候都是先把對象入棧,調用方法后再出棧。
對于使用對象調用靜態方法時(紫色框),先入棧再出棧,再調用,這樣相當于多了兩個無效的操作。所以如果要調用靜態方法時,推薦使用類調用。

多態的原理

public class Demo06 {public static void test(Animal animal) {animal.eat();System.out.println(animal);}public static void main(String[] args) throws IOException {test(new Cat());test(new Dog());System.in.read();}
}abstract class Animal {public abstract void eat();@Overridepublic String toString() {return "我是" + this.getClass().getSimpleName();}
}
class Dog extends Animal {public void eat() {System.out.println("啃骨頭");}
}class Cat extends Animal {public void eat() {System.out.println("吃魚");}
}

運行時的內存狀態:

  1. test(new Cat())調用時:
    • 堆中創建Cat對象
    • 方法區中Cat類的虛方法表(vtable)包含:
      • eat() -> Cat.eat()
      • toString() -> Animal.toString()
  2. 方法調用過程:
    • JVM通過對象頭中的類指針找到Cat
    • 通過虛方法表找到實際要調用的eat()實現
    • toString()調用則直接使用Animal中的實現

finally案例1

public class Demo07 {public static void main(String[] args) {int i = 0;try {i = 10;}catch (Exception e) {i = 20;}finally {i = 30;}}
}

finally中的代碼會被復制三份,分別放入:try分支、catch能被匹配到的分支、catch不能被匹配到的分支,確保他一定被執行。
JVM使用異常表(Exception table)來確定異常處理跳轉位置,每個條目定義了受保護的代碼范圍(from-to)、處理代碼位置(target)和異常類型
在這里插入圖片描述

finally案例2

public class Demo07 {public static int test() {try{int i = 1/0;return 10;}finally {return 20;}}public static void main(String[] args) {System.out.println(test()); // 20}
}

字節碼如下:

public static int test();Code:0: iconst_1          // 將1壓入棧1: iconst_0          // 將0壓入棧2: idiv              // 執行除法(1/0),這里會拋出ArithmeticException3: istore_0          // (不會執行)存儲結果到局部變量04: bipush 10         // (不會執行)將10壓入棧6: istore_1          // (不會執行)存儲到局部變量1(臨時返回值)7: bipush 20         // finally塊開始:將20壓入棧9: ireturn           // 直接從finally塊返回20// 異常處理部分10: astore_2          // 異常對象存儲到局部變量211: bipush 20         // finally塊:將20壓入棧13: ireturn           // 從finally塊返回20Exception table:from    to  target type0     7    10   any

finally塊中的return會完全覆蓋try塊中的return或拋出的異常,這題輸出20而不會拋異常。(原本的ArithmeticException被丟棄,因為finally中有return)
控制流變化:

  • 正常情況下:try → finally(return)
  • 異常情況下:try → catch → finally(return)
  • 兩種路徑最終都執行finally中的return

fianlly 案例3

public class Demo08 {public static int test() {int i = 10;try{return i;}finally {i = 20;}}public static void main(String[] args) {System.out.println(test()); // 10}
}

如果在try中return值了,就算在finally中修改了這個值,返回的結果也仍然不會改變,因為在return之前會先做一個暫存(固定返回值),然后執行finally中的代碼,再把暫存的值恢復到棧頂, 返回的還是之前暫存的值。在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85014.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85014.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85014.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android及Harmonyos實現圖片進度顯示效果

鴻蒙Harmonyos實現&#xff0c;使用ImageKnife自定義transform來實現圖片進度效果 import { Context } from ohos.abilityAccessCtrl; import { image } from kit.ImageKit; import { drawing } from kit.ArkGraphics2D; import { GrayScaleTransformation, PixelMapTransform…

linux 中的自動化之systemd

linux | 自動化之systemd linux 中的自動化之systemd 學習總是循序漸進的。 linux 中程序的自動化&#xff0c;包括早期手動啟動&#xff0c;查看啟動后的日志、分析啟動情況&#xff0c;再到后面封裝腳本(大致要求啟動后檢查是否掛了&#xff0c;掛了拉起&#xff0c;沒掛跳過…

【編譯工具】CodeRider 2.0:馭碼 CodeRider 2.0 全流程智能研發協作平臺深度技術測評報告

目錄 前言&#xff1a;CodeRider 2.0 簡介 &#xff08;1&#xff09;核心功能 &#xff08;2&#xff09;適用場景 &#xff08;3&#xff09;優勢 一、產品概述與技術架構 &#xff08;1&#xff09;產品定位與核心價值 &#xff08;2&#xff09;技術架構解析 &…

抓包 TCP 四次揮手報文

文章目錄 抓包 TCP 三次握手報文一、第一次揮手二、第二次揮手三、第三次揮手四、第四次揮手 抓包 TCP 三次握手報文 抓包 TCP 三次握手報文 一、第一次揮手 二、第二次揮手 三、第三次揮手 四、第四次揮手

KMP(Kotlin Multiplatform)發布Web版本亂碼

一、背景 最近用KMP嘗試運行在Android、iOS、desktop都成功了&#xff0c;網絡數據訪問也正常。 可是當運行wasmJs的時候遇到了2個較大的問題。 中文字體出現亂碼。 出現了跨域問題。 首先貼一下每個平臺的運行截圖&#xff1a; Android iOS Desktop 二、問題 當web跑起…

一個應用程序或移動網站項目提供最佳UI解決方案

一個應用程序或移動網站項目提供最佳UI解決方案 此套件是用大量的愛和辛勤工作制作的&#xff0c;為您的下一個應用程序或移動網站項目提供最佳解決方案。120個完全可編輯的界面&#xff0c;分為10個類別和2種文件格式&#xff0c;Photoshop和AI。簡單易用的結構將允許您以所…

Android studio打包生成jar包文件

Android studio打包生成jar包文件 一 項目配置1.修改 app/build.gradle2.修改 AndroidManifest.xml 二 打 Jar 包1.修改 app/build.gradle2.編譯生成 Jar 包 一 項目配置 1.修改 app/build.gradle 將com.android.application改成com.android.library注釋掉applicationId 2.…

JAVA類加載機制(jdk8)

三句話總結JDK8的類加載機制&#xff1a; 類緩存&#xff1a;每個類加載器對他加載過的類都有一個緩存。雙親委派&#xff1a;向上委托查找&#xff0c;向下委托加載。沙箱保護機制&#xff1a;不允許應用程序加載JDK內部的系統類。 JDK8的類加載體系 類加載器的核心方法 //…

更進一步深入的研究ObRegisterCallBack

引入 我們如果想hook對象的回調,在上篇文章里我們已經知道了對象回調函數存在一個列表里面&#xff0c;我們通過dt可以看見&#xff0c;這里他是一個LIST_ENTRY結構&#xff0c;但是實際調用的時候&#xff0c;這樣是行不通的&#xff0c;說明它結構不對 0: kd> dt _OBJEC…

Nginx-3 Nginx 的負載均衡策略

Nginx-3 Nginx 的負載均衡策略 Nginx 的負載均衡其實就是指將請求按照一定的策略轉發給服務集群中的一臺&#xff0c;提高了服務集群的可用性&#xff0c;解決數據流量過大、網絡負荷過重的問題。 AKF 擴展立方體 分為 3 個方向負載&#xff1a; x 軸&#xff1a;增加實例數…

Wiiu平臺RetroArch全能模擬器美化整合包v1.18

這款WiiU平臺RetroArch全能模擬器美化整合包v1.18的亮點包括&#xff1a; 1. 18款平臺完美兼容&#xff1a;無論你是喜歡NES時代的經典游戲&#xff0c;還是鐘愛SNES、GBA等平臺的大作&#xff0c;這款整合包都能滿足你的需求&#xff0c;讓你盡情暢玩游戲。 2. 三款自制主題&a…

MyBatis原理

Mybatis執行過程為&#xff1a;接口代理->sqlSession會話->executor執行器->JDBC操作 一、接口代理 Mybatis根據Mapper接口&#xff0c;動態生成相應實現類 二、SqlSession介紹 MyBatis核心對象SqlSession介紹 - MyBatis中文官網 三、Executor執行器介紹 精通My…

升級內核4.19-腳本

#bash cd /root yum remove -y kernel-tools-3.10.0-1160.el7.x86_64 yum remove -y kernel-tools-libs-3.10.0-1160.el7.x86_64tar -xvf rhel-7-amd64-rpms.tar.gz cd /root/rhel-7-amd64-rpms #安裝依賴、包括socat&conntrack yum localinstall -y *.rpm --skip-broken#升…

全面理解 JVM 垃圾回收(GC)機制:原理、流程與實踐

JVM 的 GC&#xff08;Garbage Collection&#xff09;機制是 Java 程序性能的關鍵支柱。本文將從堆內存布局、回收原理、GC 算法、流程細節、并發收集器機制等維度&#xff0c;系統講清楚 GC 的底層運作原理和優化思路。 一、JVM 堆內存結構 Java 堆是 GC 管理的主要區域&am…

runas命令讓其他用戶以管理員權限運行程序

RUNAS 用法: RUNAS使用示例&#xff1a; runas /noprofile /user:mymachine\administrator cmd #本機Administrator管理員身份執行CMD&#xff0c;/noprofile為不加載該用戶的配置信息。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc” #本機上…

實戰指南:部署MinerU多模態文檔解析API與Dify深度集成(實現解析PDF/JPG/PNG)

MinerU web api部署 MinerU 能夠將包含圖片、公式、表格等元素的多模態 PDF、PPT、DOCX 等文檔轉化為易于分析的 Markdown 格式。 克隆 MinerU 的倉庫 git clone https://github.com/opendatalab/MinerU.gitcd 到 projects/web-api cd projects/web-api在可以科學上網的情況下…

向量外積與秩1矩陣的關系

向量外積與秩1矩陣的關系 flyfish 向量外積是構造秩1矩陣的基本工具&#xff0c;其本質是用兩組向量的線性組合刻畫矩陣的行和列相關性&#xff1b;任意秩1矩陣必可表示為外積&#xff0c;而低秩矩陣&#xff08;秩 k k k&#xff09;可分解為 k k k 個外積矩陣的和&#x…

設計模式-創建型模式(詳解)

創建型模式 單例模式 一個類只允許創建一個對象&#xff0c;稱為單例。 單例體現&#xff1a;配置類、連接池、全局計數器、id生成器、日志對象。 懶漢式 (線程不安全) 單例&#xff1a;【不可用】 用到該單例對象的時候再創建。但存在很大問題&#xff0c;單線程下這段代…

什么是BI?有哪些應用場景

BI&#xff08;Business Intelligence&#xff0c;商業智能&#xff09;是通過技術手段對海量業務數據進行采集、整合、分析和可視化的過程&#xff0c;旨在幫助企業從數據中獲取洞察&#xff0c;支持決策。其核心是通過工具&#xff08;如Quick BI&#xff09;將原始數據轉化為…