簡體   English   中英

Java 中的符號引用

[英]Symbolic references in Java

這些天我一直在玩 Java 反射和.class格式。 我目前正在學習ldc教學。

在 JVM Specification 我發現 term I don't understand: symbolic reference ,我有以下問題。

  1. 這是什么意思?

  2. 它在哪里使用?

  3. ldc指令在哪些情況下加載符號引用?
  4. Java 中是否有與該操作對應的代碼?

如果您引用給您帶來麻煩的文檔的確切內容,將很有幫助。 既然您還沒有,那么我將對ldc的文檔中您可能引用的內容進行猜測

否則,如果運行時常量池條目是對類的符號引用(第5.1節),則將解析命名的類(第5.4.3.3.1節),並將對表示該類值的Class對象的引用推送到操作數堆棧。

否則,運行時常量池條目必須是對方法類型或方法句柄的符號引用(第5.1節)。 ...

此引號具有指向JVM規范(5.1)另一部分的鏈接,該部分描述了運行時常量池:

一種運行時數據結構,可滿足常規編程語言實現的符號表的許多目的

這意味着運行時常量池以符號形式包含有關類片段的信息:作為文本值。

因此,當給ldc一個類的“符號引用”時, ldc被給定了常量池中CONSTANT_Class_info結構的索引。 如果查看此結構的定義,將會看到它包含對類名的引用,該類名也保存在常量池中。

TL; DR: “符號引用”是可用於檢索實際對象的字符串。


一個例子:

if (obj.getClass() == String.class) {
    // do something
}

變為以下字節碼:

aload_1
invokevirtual   #21; //Method java/lang/Object.getClass:()Ljava/lang/Class;
ldc     #25; //class java/lang/String
if_acmpne       20

在這種情況下, ldc操作引用的是符號存儲的類。 當JVM執行此操作碼時,它將使用符號引用來標識當前類加載器中的實際類,並返回對該類實例的引用。

要添加到問題的第 1 部分和第 2 部分的其他答案:

目前,JVM 使用的運行時數據區可分為六個區域: • 程序計數器 (PC) 寄存器 • Java 虛擬機 (JVM) 堆棧 • 本機方法堆棧 • 堆區 • 方法區 • 運行時常量池

PC寄存器用來存放下一條指令的地址,也就是要執行的指令代碼。 執行引擎讀取下一條指令,JVM 使用它來跟蹤線程的執行,因為 CPU 會不斷地在它們之間切換。

棧幀由三部分組成:局部變量數組、操作數棧和幀數據。

  1. 操作數堆棧的目的是用於可能需要的任何中間操作,例如數字的加法或減法。 操作數堆棧充當運行時工作區來執行操作。
  2. 局部變量數組包含方法的所有參數和局部變量。
  3. 幀數據:包含所有符號引用(常量池解析)和與該特定方法相關的正常方法返回。

當 Java 虛擬機的實現使用常規堆棧(俗稱“C 堆棧”)來支持本機方法(以 Java 編程語言以外的語言編寫的方法)時,使用本機堆棧。

堆是運行時數據區域,所有 class 實例和 arrays 的 memory 從該區域中分配。

Java 虛擬機具有在所有 Java 虛擬機線程之間共享的方法區域。 方法區類似於傳統語言的編譯代碼的存儲區,或者類似於操作系統進程中的“文本”段。 它存儲每個類的結構,例如運行時常量池、字段和方法數據,以及方法和構造函數的代碼,包括 class 和接口初始化和實例初始化中使用的特殊方法(第 2.9 節)。

運行時常量池是 class 文件(第 4.4 節)中 constant_pool 表的按類或按接口運行時表示

那么這實際上意味着什么呢? 我的理解是,它是引用類的抽象,沒有任何副作用。

例如,如果您在運行時使用 Class object 而不是用於存儲 class 結構的常量池符號並且它沒有正確加載怎么辦? 我見過一個代碼庫,其中包含多個相同名稱、類型和版本的 jars,它們都是從代碼的不同區域加載的,因為多個團隊在一個大型代碼庫的不同部分工作,並沒有費心清理混亂. 我還使用字符串元組作為在運輸程序中構建獨特位置的一種方法。 它有效,但它似乎是處理用例的笨拙機制。 所以我的觀點是,據我所知,類和接口的符號表示滿足了這一需求,並創建了一個標准過程來在運行時引用類,而沒有副作用。

引用Brian Goetz的話:

諸如字節碼生成之類的活動經常需要描述諸如類之類的常量。 但是,對於任意 class,Class object 描述不佳。 生產一個 Class 實例有很多環境依賴和故障模式; 加載可能會失敗,因為所需的 class 不存在或請求者可能無法訪問,加載結果因 class 加載上下文而異,加載類有副作用,有時可能根本不可能(例如當所描述的類尚不存在或無法加載,例如在編譯這些相同的類期間或在 jlink 時間轉換期間。)因此,雖然字符串 class 是對 Constant_String_info 的良好描述,但 Class 類型不是對 Constant_Class_info 的很好描述。

許多活動都需要以純粹的名義形式處理類、方法和其他實體。 字節碼解析和生成庫必須以符號形式描述類和方法句柄。 如果沒有官方機制,他們必須求助於 ad-hoc 機制,無論是像 ASM 的句柄這樣的描述符類型,還是字符串元組(方法所有者、方法名稱、方法描述符),或者這些的 ad-hoc(和容易出錯)編碼到單個字符串。 通過旋轉字節碼(例如 LambdaMetafactory)操作的 invokedynamic 引導程序更喜歡在符號域中工作,而不是使用實時類和方法句柄。 編譯器和離線轉換器(例如 jlink 插件)需要為無法加載到正在運行的 VM 中的類描述類和成員。 編譯器插件(例如注釋處理器)同樣需要用符號術語來描述程序元素。 他們都會受益於用一種單一的、官方的方式來描述這些常量。

引用和闡述的 Java 文檔來自Java SE 12

有圖表可以說明這一點: Dzone 的 JVM 架構。

最后是Baeldung 的 JVM 中的常量池簡介,它用字節碼說明了一個簡單的 Hello World 程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM