深入解析JVM字節碼執行引擎

JVM 字節碼執行引擎。它是 JVM 核心組件之一,負責實際執行加載到內存中的字節碼指令。你可以將它想象成 JVM 的“CPU”。

核心職責:

  1. 加載待執行的字節碼: 從方法區(元空間)獲取已加載類的方法字節碼。
  2. 創建和管理棧幀: 在方法調用時,在 Java 虛擬機棧上為該方法創建一個棧幀,用于存儲該方法的執行狀態和數據。
  3. 解釋執行: 讀取字節碼指令,逐條解釋并執行其對應的本地機器碼操作。
  4. 即時編譯: 識別熱點代碼(頻繁執行的代碼),將其編譯成本地機器碼(Native Code)并緩存,后續執行直接運行高效的機器碼。
  5. 處理結果: 執行完成后,處理返回值(如果有),銷毀棧幀,返回調用點。

關鍵概念與工作機制:

1. 棧幀 (Stack Frame)

  • 本質: 是 JVM 進行方法調用方法執行的數據結構。每次方法調用,都會創建一個新的棧幀并壓入當前線程的 Java 虛擬機棧 (Java Virtual Machine Stack)。方法執行結束(無論正常返回還是異常拋出),其棧幀會被彈出并銷毀。
  • 構成: 一個棧幀包含以下幾個核心部分:
    • 局部變量表 (Local Variable Array):
      • 一個數組,用于存儲方法參數方法內部定義的局部變量
      • 索引從 0 開始。
      • longdouble2 個槽位 (Slot),其他基本類型 (int, float, char, short, byte, boolean, reference) 和 returnAddress1 個槽位
      • 方法參數按順序排在局部變量表的前面(static 方法第 0 位是第一個參數;實例方法第 0 位是 this 引用,然后是參數)。
    • 操作數棧 (Operand Stack):
      • 一個后進先出 (LIFO) 的棧結構。
      • 字節碼指令執行的主要工作場所。
      • 指令從操作數棧彈出 (Pop) 操作數進行計算,再將結果壓入 (Push) 棧頂。
      • 例如,iadd 指令會彈出棧頂兩個 int 值相加,再將結果 int 值壓入棧頂。
      • 其深度在編譯期就已確定(存儲在方法的 Code 屬性中)。
    • 動態鏈接 (Dynamic Linking):
      • 棧幀內部包含一個指向運行時常量池 (Runtime Constant Pool) 中該棧幀所屬方法符號引用的指針。
      • 在方法執行過程中,需要將符號引用(如調用的方法名、字段名)解析 (Resolve) 為實際的直接引用(方法入口地址、字段偏移量)。
      • 動態的含義在于,這個解析過程可以在類加載的解析階段完成,也可以在第一次使用該符號引用時才完成(延遲解析)。
    • 方法返回地址 (Return Address):
      • 存儲方法正常完成后需要返回的位置(通常是調用該方法指令的下一條指令地址)。
      • 如果方法異常退出(未捕獲的異常),返回地址由異常處理器表 (Exception Table) 確定。
    • 附加信息 (可選): 一些虛擬機實現可能包含調試信息、性能監控數據等。

2. 基于棧的指令集架構

  • JVM 字節碼指令集是 基于棧 (Stack-Based) 的,而不是基于寄存器 (Register-Based) 的(如 x86、ARM 匯編)。
  • 優勢:
    • 可移植性: 不依賴特定硬件的寄存器數量和結構,指令更緊湊(一個字節操作碼)。
    • 簡單性: 編譯器生成字節碼更簡單(只需考慮棧操作)。
    • 實現簡單: 解釋器或 JIT 編譯器實現相對容易。
  • 劣勢:
    • 執行效率: 完成相同操作通常需要更多指令(頻繁的入棧、出棧操作)。
    • 優化難度: 棧操作隱含了更多數據依賴關系,增加了編譯器優化的復雜度(但 JIT 可以克服)。

3. 字節碼解釋執行

  • 過程: 執行引擎包含一個字節碼解釋器
    1. 定位當前要執行的字節碼指令(程序計數器 PC 指向它)。
    2. 讀取操作碼 (Opcode)。
    3. 根據操作碼找到對應的操作(本地機器碼片段或微程序)。
    4. 如果需要操作數,從操作數棧彈出。
    5. 執行操作。
    6. 將結果(如果有)壓入操作數棧。
    7. 更新 PC 指向下一條指令。
  • 優點: 啟動快,內存占用相對小。
  • 缺點: 執行速度慢(每條指令都需要取指、解碼、執行本地操作)。

4. 即時編譯器 (Just-In-Time Compiler - JIT)

  • 目的: 解決解釋執行效率低的問題。將熱點代碼 (Hot Spot Code) - 頻繁執行的方法或循環體 - 動態編譯成本地機器碼,后續執行直接運行高效的機器碼。
  • 工作流程:
    1. 監控: JVM 啟動時,解釋器執行所有代碼,同時 Profiler 監控代碼執行頻率。
    2. 識別熱點: 當某個方法或代碼塊的調用/執行次數超過閾值(-XX:CompileThreshold),它就被標記為熱點代碼。
    3. 編譯排隊: 熱點代碼被提交給 JIT 編譯器線程進行編譯。
    4. 編譯: JIT 編譯器將字節碼編譯成本地機器碼。
    5. 緩存: 編譯后的機器碼存儲在 Code Cache 區域(位于堆外內存)。
    6. 替換: 該方法的入口地址被替換為指向編譯好的機器碼。
    7. 執行: 后續對該方法的調用直接執行本地機器碼,無需解釋。
  • HotSpot VM 的 JIT 編譯器:
    • C1 編譯器 (Client Compiler / -client):
      • 優化較少,編譯速度快。
      • 關注局部優化(如方法內聯、去虛擬化、冗余消除)。
      • 適合桌面應用或對啟動速度敏感的場景。
    • C2 編譯器 (Server Compiler / -server):
      • 優化激進,編譯速度慢。
      • 進行大量全局優化(如逃逸分析、循環展開、鎖消除)。
      • 生成代碼執行效率高。
      • 適合服務器端長期運行的應用。
    • 分層編譯 (Tiered Compilation - -XX:+TieredCompilation, Java 7+ 默認):
      • 結合 C1 和 C2 的優勢。
      • 代碼首先被解釋執行 (Level 0)。
      • 達到一定調用次數,由 C1 快速編譯,開啟簡單優化 (Level 1, 2, 3)。
      • 如果方法調用非常頻繁(成為“更熱的點”),再交給 C2 進行深度優化編譯 (Level 4)。
      • 目標: 在啟動速度和峰值性能之間取得最佳平衡。
  • JIT 關鍵技術:
    • 方法內聯 (Method Inlining): 將被調用方法的代碼“復制”到調用方法中,消除方法調用的開銷(壓棧、跳轉、彈棧)。最重要的優化之一!
    • 逃逸分析 (Escape Analysis): 分析對象的作用域。
      • 如果對象不會逃逸出方法或線程(即僅在方法內部使用,或只被當前線程訪問),則可進行優化:
        • 棧上分配 (Scalar Replacement): 將對象拆解成基本類型,直接在棧上分配其成員變量,避免堆分配開銷和 GC 壓力。
        • 同步消除 (Lock Elision): 如果對象不會逃逸到其他線程,對其進行的同步操作(synchronized)可以移除。
    • 公共子表達式消除 (Common Subexpression Elimination): 消除重復計算。
    • 循環展開 (Loop Unrolling): 減少循環條件判斷次數。
    • 去虛擬化 (Devirtualization): 將虛方法調用(invokevirtual, invokeinterface)轉換為直接調用(invokespecial, invokestatic)或靜態調用,消除動態分派開銷。基于類層次分析 (CHA)。

5. 方法調用與分派

  • 字節碼中調用方法使用特定的指令:
    • invokestatic: 調用靜態方法。
    • invokespecial: 調用構造方法 (<init>)、私有方法、父類方法 (super.method())。靜態綁定
    • invokevirtual: 調用對象的實例方法(最常見的虛方法調用)。動態綁定
    • invokeinterface: 調用接口方法。動態綁定
    • invokedynamic (Java 7+): 動態語言支持(如 Lambda 表達式、方法引用),由 bootstrap 方法在運行時動態解析調用點。最靈活的綁定
  • 靜態分派 (Static Dispatch): 依賴靜態類型 (Static Type / Apparent Type) 進行方法版本選擇。發生在編譯期。典型應用:方法重載 (Overload)
    class Human {}
    class Man extends Human {}
    class Woman extends Human {}public void sayHello(Human guy) { System.out.println("Hello, guy!"); }
    public void sayHello(Man guy) { System.out.println("Hello, gentleman!"); }
    public void sayHello(Woman guy) { System.out.println("Hello, lady!"); }Human man = new Man(); // 靜態類型是Human, 實際類型(運行時類型)是Man
    sayHello(man); // 輸出 "Hello, guy!"。編譯期根據靜態類型Human確定調用sayHello(Human)
    
  • 動態分派 (Dynamic Dispatch): 依賴實際類型 (Actual Type / Runtime Type) 進行方法版本選擇。發生在運行期。典型應用:方法重寫 (Override)。通過虛方法表 (vtable) 實現(invokevirtual, invokeinterface)。
    abstract class Animal {abstract void makeSound();
    }
    class Dog extends Animal { void makeSound() { System.out.println("Woof!"); } }
    class Cat extends Animal { void makeSound() { System.out.println("Meow!"); } }Animal animal = new Dog(); // 實際類型是Dog
    animal.makeSound(); // 輸出 "Woof!"。運行期根據實際類型Dog查找Dog的makeSound方法
    animal = new Cat();
    animal.makeSound(); // 輸出 "Meow!"。運行期根據實際類型Cat查找Cat的makeSound方法
    

6. 執行引擎如何與內存交互

  • 棧幀管理: 在 Java 虛擬機棧上分配和銷毀,存儲方法執行狀態(局部變量、操作數棧)。
  • 堆 (Heap): 執行引擎通過字節碼指令(如 new, getfield, putfield, arraylength)在堆上創建和操作對象/數組。對象字段的訪問通過解析后的直接引用(偏移量)進行。
  • 方法區 (Metaspace): 存儲已被加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼緩存 (Code Cache)。執行引擎從這里讀取要執行的字節碼和符號引用(后續解析)。
  • 程序計數器 (PC Register): 每個線程私有,指向當前線程正在執行的字節碼指令地址。執行引擎依賴它知道下一條要執行的指令。

總結:

JVM 字節碼執行引擎是 Java 程序運行的動力核心,它通過:

  1. 棧幀管理: 為每個方法調用創建獨立上下文(局部變量表、操作數棧等)。
  2. 基于棧的指令集: 定義了可移植但相對低效的執行方式。
  3. 解釋執行: 提供快速啟動能力。
  4. 即時編譯 (JIT): 將熱點代碼編譯成本地機器碼,大幅提升執行效率(C1/C2/分層編譯 + 多種優化如內聯、逃逸分析)。
  5. 方法調用與分派: 正確處理靜態分派(重載/編譯期)和動態分派(重寫/運行期/虛方法表)。
  6. 內存交互: 與 JVM 內存區域(堆、棧、方法區、PC)緊密協作完成數據存取和指令執行。

正是解釋器與 JIT 編譯器的高效協作,以及基于棧的靈活架構,使得 JVM 能夠在跨平臺的同時,為 Java 應用程序提供接近原生代碼的執行性能。理解執行引擎是深入掌握 JVM 工作原理和進行性能調優的關鍵。

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

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

相關文章

華為OD機試-MELON的難題-DFS(JAVA 2025A卷)

題意是從N快雨花石中找出最少拿出雨花石的塊數&#xff0c;使得雨花石可以均分&#xff0c;直接使用dfs解決此類組合問題 package com.example.demo.bean;import java.util.Arrays; import java.util.LinkedList; import java.util.Scanner;public class YuHuaStone {public s…

鴻蒙數據庫操作

一、使用關系型數據庫實現數據持久化&#xff0c;需要獲取一個RdbStore&#xff0c;其中包括建庫、建表、升降級等操作。 const STORE_CONFIG: relationalStore.StoreConfig {name: AnyOffice.db, // 數據庫文件名securityLevel: relationalStore.SecurityLevel.S1, // 數據庫…

基于ARM SoC的半導體測試

ARM SoC&#xff08;System on Chip&#xff09; 是一種集成了多個關鍵計算組件的單片系統芯片&#xff0c;廣泛應用于移動設備、嵌入式系統、物聯網&#xff08;IoT&#xff09;和半導體測試設備等領域。它的核心設計理念是“高度集成”&#xff0c;將處理器、內存、外設接口等…

JavaEE->多線程2

目錄 一、線程安全&#xff08;重點&#xff09; 1.線程安全演示 2.線程不安全的原因 1.線程是搶占式執行的&#xff08;執行順序是隨機的&#xff09; 2.多個線程同時修改了同一個變量 3.原子性 4.內存可見性 5.指令重排序&#xff08;有序性&#xff09; 二、解決線…

Flutter TCP通信

啟動TCP服務 Future<void> startServer() async {final server await ServerSocket.bind(InternetAddress.anyIPv4, 12345);print(Server listening on ${server.address}:${server.port});server.listen((Socket socket) {print(Client connected: ${socket.remoteAddr…

flask拆分計劃

兩個啟動鏈接&#xff0c;看日志提示是因為2次啟動&#xff0c;一次是database&#xff0c;一次是xmind2&#xff0c;去掉一次就可以&#xff0c;如何去掉一次&#xff1f; 這里啟動也調用了一次&#xff0c;所以測試環境注釋掉&#xff0c;如下圖&#xff0c;也就調用了一次

【生活】ECMO原理、作用、費用及使用方法

博客目錄 一、ECMO 是什么&#xff1f;二、ECMO 的作用1. 替代肺功能&#xff08;氧合與二氧化碳清除&#xff09;2. 替代心臟功能&#xff08;循環支持&#xff09;3. 為其他治療爭取時間4. 用于心肺復蘇&#xff08;ECPR&#xff09; 三、ECMO 的費用1. 設備使用費2. 耗材費用…

Profinet轉EtherCAT網關模塊怎么用:案例分享

在某制造工廠西門子S7-1200 PLC中&#xff0c;存在一個技術難題&#xff0c;即伺服驅動器與可編程邏輯控制器&#xff08;PLC&#xff09;之間的通訊不兼容問題。具體而言&#xff0c;PLC采用的是PROFINET通訊協議&#xff0c;而伺服EtherCAT協議驅動器則需要EtherCAT協議進行數…

什么是 NLP-NLP基礎知識體系的系統認知

NLP基礎知識體系的系統認知 一、引言 今天的學習內容集中于自然語言處理&#xff08;NLP&#xff09;的基本概念、發展歷程、核心任務及文本表示技術。通過這一學習過程&#xff0c;我對NLP這門學科有了更加系統和深入的認識&#xff0c;并且理解了NLP技術的廣泛應用及其復雜…

數據結構 學習 鏈表 2025年6月14日08點01分

單向鏈表: 線性數據結構 由一系列節點組成 每個節點包含: 數據部分:存儲實際數據 指針部分:儲存指向下一個節點的引用 特點1,每個節點只有一個指向下一個節點的指針 特點2,只能從頭到尾 單向遍歷 特點3,不需要連續的內存空間 特點4,插入和刪除效率高 特點5,隨機訪問 效率低 …

使用 Kubernetes 部署 PHP 留言板應用(含 Redis 架構)

使用 Kubernetes 部署 PHP 留言板應用&#xff08;含 Redis 架構&#xff09; 文章目錄 使用 Kubernetes 部署 PHP 留言板應用&#xff08;含 Redis 架構&#xff09;教程概述技術架構特點 準備工作環境要求 Redis 數據庫部署Redis 主從架構原理創建 Redis 領導者 Deployment部…

MATLAB提供的兩種畫誤差矩陣的函數

MATLAB在統計學和機器學習工具包中提供了兩種畫誤差矩陣&#xff08;Confusion matrix&#xff09;的函數。 figure; plotconfusion(YValidation,YPred)figure; cm confusionchart(YValidation,YPred) cm.Title Confusion Matrix for Validation Data; cm.RowSummary row-n…

【Java學習筆記】泛型

泛型 一、泛型的引出 代碼示例 public class pra {public static void main(String[] args) {ArrayList arrayList new ArrayList();arrayList.add("java");arrayList.add("jack");arrayList.add("jom");arrayList.add(new a());for (Object…

SpringMVC系列(一)(介紹,簡單應用以及路徑位置通配符)

0 引言 作者正在學習SpringMVC相關內容&#xff0c;學到了一些知識&#xff0c;希望分享給需要短時間想要了解SpringMVC的讀者朋友們&#xff0c;想用通俗的語言講述其中的知識&#xff0c;希望與諸位共勉&#xff0c;共同進步&#xff01; 1 SpringMVC介紹 SpringMVC本質上…

Java中如何使用lambda表達式分類groupby

Java中如何使用lambda表達式分類groupby Java中如何使用lambda表達式分類groupby分類問題場景傳統手寫方式lambda使用groupBy()方法一行結束&#xff01;&#xff01;&#xff01;完整代碼 Java中如何使用lambda表達式分類groupby 分類問題場景 比如一群學生根據性別和年齡排…

無人機開發分享——無人機集群基于braft實現長機動態推選算法

在無人機集群項目的算法開發中&#xff0c;推選長機作為集群的動態中心&#xff0c;往往承擔著集群管理、通訊中繼等重要功能。由于通訊鏈路的有限性和任務的實時性需要&#xff0c;需要保證動態長機時刻工作正常&#xff0c;并在異常情況下快速切換新長機。 本文主要分享基于b…

python 解碼 jwt

import base64 import jsondef base64url_decode(base64url_data):# 將URL安全的base64編碼數據轉換為標準的base64編碼數據base64_data base64url_data.replace(-, ).replace(_, /)# 如果數據長度不是4的倍數&#xff0c;則補齊padding_length 4 - len(base64_data) % 4base…

騰訊云TCCA認證考試報名 - TDSQL數據庫交付運維工程師(MySQL版)

數據庫交付運維工程師-騰訊云TDSQL(MySQL版)認證 適合人群&#xff1a; 適合從事TDSQL(MySQL版)交付、初級運維、售前咨詢以及TDSQL相關項目的管理人員。 認證考試 單選*40道多選*20道 成績查詢 70分及以上通過認證&#xff0c;官網個人中心->認證考試 查詢 考試費用&am…

Spring Boot的Security安全控制——認識SpringSecurity!

Spring Boot的Security安全控制 在Web項目開發中&#xff0c;安全控制是非常重要的&#xff0c;不同的人配置不同的權限&#xff0c;這樣的系統才安全。最常見的權限框架有Shiro和Spring Security。Shiro偏向于權限控制&#xff0c;而Spring Security能實現權限控制和安全控制…

深入理解ArrayList:從Java原生實現到手寫一個ArrayList

Java原生ArrayList解析 基本結構 Java的ArrayList是基于數組實現的動態列表&#xff0c;主要特點包括&#xff1a; 動態擴容&#xff1a;當元素數量超過當前容量時&#xff0c;自動擴容&#xff08;通常增加50%&#xff09; 快速隨機訪問&#xff1a;通過索引訪問元素的時間…