JVM——JVM是怎么實現invokedynamic的?

JVM是怎么實現invokedynamic的?

在Java 7引入invokedynamic之前,Java虛擬機(JVM)在方法調用方面相對較為“僵化”。傳統的Java方法調用主要依賴于invokestatic、invokespecial、invokevirtual和invokeinterface這四條指令,每條指令都明確綁定了目標方法的類名、方法名和方法描述符。這種綁定方式雖然穩定,但對于動態語言的支持卻顯得力不從心。動態語言強調“鴨子類型”(duck typing),即只要對象表現出某種行為,就認為它符合某種類型,而不必顯式繼承某個類或實現某個接口。為了打破這種限制,Java 7引入了invokedynamic指令,為JVM上的動態語言支持鋪平了道路,同時也為Java自身的語言特性發展(如Lambda表達式)提供了強有力的支持。

invokedynamic指令的基本概念

invokedynamic指令的核心在于引入了“調用點”(CallSite)這一概念。調用點是一個抽象類,它將方法調用與目標方法的鏈接推遲到運行時進行。每個invokedynamic指令在執行時都會綁定一個調用點對象,該對象負責在運行時確定要調用的目標方法。調用點對象可以是ConstantCallSite(不可變調用點)、MutableCallSite(可變調用點)或VolatileCallSite(線程安全可變調用點)。通過調用點,JVM能夠在運行時靈活地選擇目標方法,從而支持動態類型語言的靈活調用機制。

import java.lang.invoke.*;
?
public class ConstantCallSiteDemo {public static void main(String[] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType methodType = MethodType.methodType(void.class);MethodHandle methodHandle = lookup.findStatic(ConstantCallSiteDemo.class, "hello", methodType);CallSite callSite = new ConstantCallSite(methodHandle);((ConstantCallSite) callSite).dynamicInvoker().invokeExact();}
?public static void hello() {System.out.println("Hello, World!");}
}

invokedynamic的底層實現

(一)啟動方法(Bootstrap Method)

當JVM第一次遇到invokedynamic指令時,它會執行該指令關聯的啟動方法。啟動方法是一個特殊的靜態方法,它負責創建并返回一個CallSite對象。啟動方法的第一個參數必須是MethodHandles.Lookup對象,用于提供對類成員的訪問權限;第二個參數是目標方法的名稱(String類型);第三個參數是MethodType,表示目標方法的類型。除此之外,啟動方法還可以接受其他參數,用于輔助生成CallSite對象。

import java.lang.invoke.*;
?
public class BootstrapDemo {public static void main(String[] args) throws Throwable {// 創建方法句柄MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType type = MethodType.methodType(void.class);MethodHandle target = lookup.findStatic(BootstrapDemo.class, "hello", type);
?// 創建調用點CallSite callSite = new ConstantCallSite(target);
?// 獲取動態調用的入口點MethodHandle dynamicInvoker = callSite.dynamicInvoker();
?// 動態調用dynamicInvoker.invokeExact();}
?public static void hello() {System.out.println("Hello, Invokedynamic!");}
}

啟動方法返回一個CallSite對象,這個對象將與invokedynamic指令綁定,并在后續調用中直接使用。

(二)動態鏈接

調用點對象中的動態鏈接過程涉及到方法句柄(MethodHandle)的使用。方法句柄是一個強類型的引用,可以直接執行。它可以指向靜態方法、實例方法、構造函數,甚至是字段的getter和setter方法(在方法句柄中表現為虛構方法)。與反射不同,方法句柄的權限檢查在創建時完成,后續調用無需重復檢查,因此性能更高。

import java.lang.invoke.*;
?
public class MethodHandleDemo {public static void main(String[] args) throws Throwable {// 創建方法句柄MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType type = MethodType.methodType(void.class);MethodHandle methodHandle = lookup.findStatic(MethodHandleDemo.class, "hello", type);
?// 調用方法句柄methodHandle.invokeExact();}
?public static void hello() {System.out.println("Hello, Method Handle!");}
}

(三)Lambda 表達式與 invokedynamic

Lambda 表達式的出現是 Java 8 的一個重大更新,它允許開發者以更簡潔的方式編寫匿名類。Lambda 表達式的實現正是基于 invokedynamic 指令。當編譯器遇到 Lambda 表達式時,會將其轉換為一個函數式接口的實例。這個轉換過程通過 invokedynamic 指令完成,編譯器會生成一個 bootstrap 方法,該方法在運行時生成一個適配器類,實現對應的函數式接口。

例如,以下代碼:

Comparator<String> comparator = (a, b) -> a.compareTo(b);

編譯器會將其轉換為類似如下的 invokedynamic 指令:

aload_1
invokedynamic #5, 0 // BootstrapMethod

對應的 bootstrap 方法會生成一個實現 Comparator 接口的適配器類,并返回該類的實例。

import java.lang.invoke.*;
?
public class LambdaBootstrap {public static void main(String[] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup();MethodHandle mh = lookup.findStatic(LambdaBootstrap.class, "lambdaImpl", MethodType.methodType(void.class, String.class));CallSite callSite = new ConstantCallSite(mh.asType(MethodType.methodType(void.class, Object.class)));((ConstantCallSite) callSite).dynamicInvoker().invokeExact("Hello, Lambda!");}
?private static void lambdaImpl(String s) {System.out.println(s);}
}

三、invokedynamic的性能分析

(一)方法句柄的性能

方法句柄的性能在某些場景下接近直接方法調用。通過將方法句柄存儲在 final 靜態變量中,即時編譯器可以對其進行內聯優化,從而消除方法句柄調用的開銷。然而,如果方法句柄被頻繁更新或無法被識別為常量,其性能可能接近反射調用,存在一定的性能開銷。

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
?
public class MethodHandlePerformance {private static final MethodHandle MH;
?static {try {MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType type = MethodType.methodType(void.class, int.class);MH = lookup.findStatic(MethodHandlePerformance.class, "target", type);} catch (Throwable e) {throw new RuntimeException(e);}}
?public static void main(String[] args) throws Throwable {long start = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {MH.invokeExact(42);}System.out.println("MethodHandle: " + (System.currentTimeMillis() - start) + " ms");}
?public static void target(int i) {// 空方法}
}

(二)Lambda 表達式的性能

Lambda 表達式的性能在大多數情況下接近直接方法調用。對于未捕獲變量的 Lambda 表達式,即時編譯器可以內聯其調用,性能與直接調用幾乎無差異。而對于捕獲變量的 Lambda 表達式,即時編譯器的逃逸分析可以優化掉適配器實例的創建,使其性能接近未捕獲變量的 Lambda 表達式。然而,在逃逸分析無法生效的情況下,可能會產生適配器實例的創建開銷,性能會有所下降。

import java.util.function.IntConsumer;
?
public class LambdaPerformance {public static void main(String[] args {int x = 42;long start = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {((IntConsumer) (j) -> {// 空實現}).accept(42);}System.out.println("Lambda: " + (System.currentTimeMillis() - start) + " ms");}
}

invokedynamic的實際應用與優勢

(一)動態語言支持

invokedynamic為動態語言在JVM上的實現提供了基礎支持。動態語言(如Groovy、JavaScript等)可以利用invokedynamic實現高效的動態方法調用,而無需通過反射或復雜的橋接代碼。例如,在Groovy中,方法調用可以通過invokedynamic直接鏈接到目標方法,而無需顯式的類型檢查和方法查找,從而提高性能。

// Groovy示例
def hello(name) {println "Hello, $name!"
}hello "Invokedynamic"

(二)Java 8 的 Lambda 表達式

如前所述,Java 8 的 Lambda 表達式借助 invokedynamic 實現了簡潔高效的語法糖。Lambda 表達式可以被轉化為函數式接口的實例,而無需顯式地實現接口。這不僅簡化了代碼,還提高了性能,因為 invokedynamic 允許即時編譯器對 Lambda 表達式進行內聯優化。

import java.util.function.Consumer;public class LambdaExample {public static void main(String[] args) {Consumer<String> consumer = (s) -> {System.out.println(s);};consumer.accept("Hello, Lambda!");}
}

(三)函數式編程

invokedynamic 支持函數式編程風格,使得 Java 開發者能夠更方便地編寫函數式代碼。通過 Lambda 表達式和方法引用,開發者可以將行為(函數)作為參數傳遞給方法,或者將方法的結果作為函數返回。這種編程風格在處理集合操作(如流式 API)時尤為強大。

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;public class FunctionalExample {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie");Consumer<String> printer = (name) -> System.out.println(name);names.forEach(printer);}
}

總結

invokedynamic 指令作為 Java 7 引入的一項革命性特性,為 JVM 帶來了前所未有的靈活性和動態性。通過調用點(CallSite)和方法句柄(MethodHandle)的機制,invokedynamic 允許在運行時動態確定方法調用的目標,從而打破了傳統方法調用的靜態綁定限制。這不僅為動態語言在 JVM 上的高效實現鋪平了道路,也為 Java 自身的語言發展注入了新的活力,使得諸如 Lambda 表達式等現代編程特性得以實現。在性能方面,盡管 invokedynamic 在某些復雜場景下可能存在一定的開銷,但即時編譯器的優化(如內聯和逃逸分析)在大多數情況下能夠使其性能接近甚至媲美直接方法調用。對于開發者而言,理解 invokedynamic 的工作原理有助于更好地利用 Java 8 及更高版本中的新特性,編寫出更簡潔、高效且具有函數式風格的代碼,同時也為探索 JVM 上的動態語言世界打開了一扇大門。

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

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

相關文章

STM32教程:ADC原理及程序(基于STM32F103C8T6最小系統板標準庫開發)*詳細教程*

前言: 本文章介紹了STM32微控制器的ADC外設,介紹了ADC的底層原理以及基本結構,介紹了ADC有關的標準庫函數,以及如何編寫代碼實現ADC對電位器電壓的讀取。 可以根據基本結構圖來編寫代碼 大體流程: 1、開啟RCC時鐘(包括ADC和GPIO的時鐘,另外ADCCLK的分頻器,也需要配置…

2025年APP安全攻防指南:抵御DDoS與CC攻擊的實戰策略

2025年&#xff0c;隨著AI技術與物聯網設備的深度滲透&#xff0c;DDoS與CC攻擊的復雜性和破壞性顯著升級。攻擊者通過偽造用戶行為、劫持智能設備、利用協議漏洞等手段&#xff0c;對APP發起精準打擊&#xff0c;導致服務癱瘓、用戶流失甚至數據泄露。面對這一挑戰&#xff0c…

STM32的定時器

定時器的介紹 介紹&#xff1a;STM32F103C8T6微控制器內部集成了多種類型的定時器&#xff0c;這些定時器在嵌入式系統中扮演著重要角色&#xff0c;用于計時、延時、事件觸發以及PWM波形生成、脈沖捕獲等應用。 *幾種定時器&#xff08;STM32F103系列&#xff09;&#xff1…

算法中的數學:約數

1.求一個整數的所有約數 對于一個整數x&#xff0c;他的其中一個約數若為i&#xff0c;那么x/i也是x的一個約數。而其中一個約數的大小一定小于等于根號x&#xff08;完全平方數則兩個約數都為根號x&#xff09;&#xff0c;所以我們只需要遍歷到根號x&#xff0c;然后計算出另…

不同OS版本中的同一yum源yum list差異排查思路

問題描述&#xff1a; qemu-guest-agent二進制rpm包的yum倉庫源和yum源倉庫配置文件path_to_yum_conf&#xff0c; 通過yum list --available -c path_to_yum_conf 查詢時&#xff0c;不同的OS版本出現了不同的結果 anolis-8無法識別 centos8可以識別 說明&#xff1a; 1 測試…

如何使用極狐GitLab 軟件包倉庫功能托管 helm chart?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 軟件包庫中的 Helm charts (BASIC ALL) WARNING:Helm chart 庫正在開發中&#xff0c;由于功能有限&#xff0c;尚未準備好用…

【PostgreSQL數據分析實戰:從數據清洗到可視化全流程】3.1 數據質量評估指標(完整性/一致性/準確性)

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 數據質量評估核心指標&#xff1a;完整性、一致性、準確性實戰解析3.1 數據質量評估指標體系3.1.1 完整性&#xff1a;數據是否存在缺失1.1.1 核心定義與業務影響1.1.2 檢測…

詳解 FFMPEG 交叉編譯 `FLAGS` 和 `INCLUDES` 的作用

FLAGS 和 INCLUDES這兩行是 Android NDK 編譯時的編譯器選項&#xff0c;用于控制代碼生成、優化、調試、安全性和頭文件搜索路徑。下面逐項詳解&#xff1a; 1. FLAGS 詳解&#xff08;編譯器選項&#xff09; FLAGS 定義了傳遞給 C/C 編譯器&#xff08;如 clang 或 gcc&…

【RK3588嵌入式圖形編程】-Cairo-Cairo圖形庫支持后端

Cairo圖形庫支持后端 文章目錄 Cairo圖形庫支持后端1、PNG圖像后端2、PDF文件后端3、SVG文件后端4、GTK窗口支持Cairo庫支持多種后端。在本文中,我們使用Cairo創建PNG圖像、PDF文件、SVG文件,并在GTK窗口上繪制。 1、PNG圖像后端 在第一個示例中,我們創建一個 PNG 圖像。 …

【常用算法:排序篇】2.快速排序的算法精要

快速排序是算法領域的"九陽神功"&#xff0c;掌握其精髓能讓你在算法修煉之路上突破瓶頸。 1. 快速排序的核心思想 快速排序&#xff08;Quicksort&#xff09;是一種基于分治思想的高效排序算法&#xff0c;核心步驟為&#xff1a; 選擇基準值&#xff08;Pivot&…

在現代Web應用中集成 PDF.js (pdfjs-dist 5.2 ESM): 通過 jsdelivr 實現動態加載與批注功能的思考

PDF 文檔在現代 Web 應用中越來越常見&#xff0c;無論是作為文檔預覽、報告展示還是在線編輯的載體。Mozilla 的 PDF.js 是一個功能強大的 JavaScript 庫&#xff0c;它使得在瀏覽器端渲染和顯示 PDF 文件成為可能&#xff0c;無需依賴原生插件。 本文將深入探討如何在你的項…

基于FPGA控制ADC0832雙通道采樣+電壓電流采樣+LCD屏幕顯示

基于FPGA控制ADC0832雙通道采樣電壓電流采樣LCD屏幕顯示 前言一、芯片手冊閱讀1.SPI通信時序 二、仿真分析三、代碼分析總結視頻演示 前言 定制 要求使用ADC0832芯片進行ADC采樣。其中電壓采樣以及電流采樣是固定電路&#xff0c;是硬件設計&#xff0c;跟軟件沒沒關系。本質上…

生產部署方案pm2配合python3腳本

前言 使用python3來處理redis 消息隊列&#xff0c;記錄下生產部署方案 「生產部署方案」&#xff1a; 多進程&#xff08;動態擴容&#xff09;無限自愈日志自動壓縮系統級守護可多隊列多worker 終極穩健版&#xff1a;PM2 Logrotate 自動擴容 守護鏈 適合&#xff1a…

Python全流程開發實戰:基于IMAP協議安全下載個人Gmail郵箱內所有PDF附件

文章目錄 一、需求分析與安全前置&#xff1a;為什么需要專用工具&#xff1f;1.1 痛點場景1.2 技術方案選擇 二、準備工作&#xff1a;Gmail賬號安全配置與環境搭建2.1 開啟兩步驗證&#xff08;必做&#xff01;&#xff09;2.2 創建應用專用密碼&#xff08;替代普通密碼&am…

巧用python之--模仿PLC(PLC模擬器)

工作中用到了VM(VisionMaster4.3)有時候需要和PLC打交道,但是PLC畢竟是別人的,不方便修改別人的程序,這時候需要一個靈活的PLC模擬器是多么好呀! 先說背景: PLC型號 匯川Easy521: Modbus TCP 192.168.1.10:502 在匯川Easy521中Modbus保持寄存器D寄存器 ,在modbus協議中 0-4區…

docker構建鏡像并上傳dockerhub

docker構建鏡像并上傳dockerhub 前提條件&#xff1a;需要連接梯子 將梯子配置到虛擬機中&#xff08;確保主機能夠連接 hub.docker.com&#xff09; 使用ipconfig 查詢主機的 ip4地址虛擬機的連接模式改成橋接模式&#xff08;復制主機的地址網絡&#xff09;將ip4配置到虛擬…

python實現的音樂播放器

python實現的音樂播放器 音樂播放器,原來寫過一個簡陋的例子,可見 https://blog.csdn.net/cnds123/article/details/137874107 那個不能拖動播放進度條上的滑塊到新的位置播放。下面介紹的可以拖動播放進度條上的滑塊到新的位置播放。 簡單實用的音樂播放器 這個簡單實用的…

[網安工具] 端口信息收集工具 —— 御劍高速 TCP 全端口掃描工具 · 使用手冊

&#x1f31f;想了解其它網安工具&#xff1f;看看這個&#xff1a;[網安工具] 網絡安全工具管理 —— 工具倉庫 管理手冊 https://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scannerhttps://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scanner 0…

數字孿生賦能智慧城市:從概念到落地的深度實踐

在城市規模與復雜度持續攀升的當下&#xff0c;傳統管理模式已難以滿足現代城市精細化治理需求。數字孿生技術憑借構建虛擬城市鏡像、實現實時數據交互與智能決策的特性&#xff0c;成為智慧城市建設的核心引擎。本文將通過多個典型案例&#xff0c;深度解析數字孿生技術如何重…

DeFi開發系統軟件開發:技術架構與生態重構

DeFi開發系統軟件開發&#xff1a;技術架構與生態重構 ——2025年去中心化金融開發的范式革新與實踐指南 一、技術架構演進&#xff1a;從單一鏈到多鏈混合引擎 現代DeFi系統開發已從單一公鏈架構轉向“跨鏈互操作混合模式”&#xff0c;結合中心化效率與去中心化安全雙重優勢…