Java 平臺,標準版Java Virtual Machine Guide

https://docs.oracle.com/en/java/javase/18/vm/index.html

Java 平臺,標準版Java Virtual Machine Guide

編譯器控制選項

選項是編譯指令。選項提供方法上下文精度。可用選項因編譯器而異,並且需要特定類型的值。

表 2-1 常用選項

選項

描述

值類型

默認值

Enable

如果設置爲 ,則隱藏指令並使其無法匹配false。此選項可用於防止選項重複。請參閱使用啓用選項防止重複

bool

true

Exclude

從編譯中排除方法。

bool

false

BreakAtExecute

在調試 JVM 時設置斷點以在指定方法的開始處停止執行。

bool

false

BreakAtCompile

調試 JVM 時,在指定方法的開頭設置斷點以停止編譯。

bool

false

Log

僅將指定的方法放置在日誌中。您必須首先設置命令行選項-XX:+LogCompilation。默認值false將所有編譯的方法放在日誌中。

bool

false

PrintAssembly

使用外部disassembler.so庫打印字節編碼和本機方法的彙編代碼。

bool

false

PrintInlining

打印哪些方法被內聯,以及在哪裏。

bool

false

PrintNMethods

在生成 nmethods 時打印它們。

bool

false

BackgroundCompilation

將方法編譯爲後臺任務。方法以解釋器模式運行,直到後臺編譯完成。該值false將方法編譯爲前臺任務。

bool

true

ReplayInline

啓用與CIReplay相應全局選項相同的功能,但基於每個方法。

bool

false

DumpReplay

啓用與CIReplay相應全局選項相同的功能,但基於每個方法。

bool

false

DumpInline

啓用與CIReplay相應全局選項相同的功能,但基於每個方法。

bool

false

CompilerDirectivesIgnoreCompileCommands

忽略所有編譯命令。

bool

false

DisableIntrinsic(adj. 內在的,固有的)

根據方法匹配標準禁用內部函數。

ccstr

沒有默認值。

inline

強制或阻止基於方法匹配標準的方法內聯。請參閱編寫內聯指令選項

ccstr[]

沒有默認值。

表 2-2 C2 獨佔選項

選項

描述

值類型

默認值

BlockLayoutByFrequency

從熱路徑移動不頻繁的執行分支。

bool

true

PrintOptoAssembly

使用外部disassembler.so庫編譯後打印生成的彙編代碼。這需要 JVM 的調試構建。

bool

false

PrintIntrinsics

打印使用了哪些內部方法以及在哪裏使用。

bool

false

TraceOptoPipelining

跟蹤流水線信息,類似於相應的全局選項,但基於每個方法。這適用於緩慢和快速的調試構建。

bool

false

TraceOptoOutput

跟蹤流水線信息,類似於相應的全局選項,但基於每個方法。這適用於緩慢和快速的調試構建。

bool

false

TraceSpilling

跟蹤變量溢出。

bool

false

Vectorize

跨向量寄存器並行執行計算。

bool

false

VectorizeDebug

跨向量寄存器並行執行計算。這需要 JVM 的調試構建。

intx

0

CloneMapDebug

使您能夠檢查CloneMap從矢量化生成的內容。這需要 JVM 的調試構建。

bool

false

IGVPrintLevel

指定在 Oracle 的 Hotspot Ideal Graphic Visualizer (IGV) 中打印編譯器圖形的點。較高的值意味着較高的粒度。

intx

0

MaxNodeLimit

設置單個方法編譯期間要使用的最大節點數。

intx

80000

編寫指令文件

單獨的編譯器指令寫在一個指令文件中。只能將指令文件而不是單個指令添加到活動指令堆棧中。

  1. 創建帶有.json擴展名的文件。指令文件是使用 JSON 語法的子集編寫的,並有少量添加和偏差。
  2. 添加以下語法作爲您可以使用的模板:

3. [  //Array of Directives4.     {   //Directive Block5.         //Directive 16.     },7.     {   //Directive Block8.         //Directive 29.     },]

該模板的組成部分是:

指令數組

  • 指令文件存儲指令塊數組,用一對括號 ( []) 表示。
  • 如果文件只包含一個指令塊,則括號是可選的。

指令塊

  • 一個塊用一對大括號 ( {}) 表示。
  • 一個塊包含一個單獨的指令。
  • 指令文件可以包含任意數量的指令塊。
  • 塊用逗號 ( ,)分隔。
  • 數組中最後一個塊後面的逗號是可選的。

指示

  • 每個指令都必須在一個指令塊內。
  • 當一個指令文件包含多個指令塊時,它可以包含多個指令。

註釋

  • 單行註釋前面有兩個斜槓 ( //)。
  • 不允許多行註釋。

以下示例顯示了包含兩個編譯器指令的完整指令文件:

[  //Array of directives    {   //Directive Block        //Directive 1        match: ["java*.*", "oracle*.*"],        c1: {            Enable: true,            Exclude: true,            BreakAtExecute: true,        },        c2: {            Enable: false,            MaxNodeLimit: 1000,        },        BreakAtCompile: true,        DumpReplay: true,    },    {   //Directive Block        //Directive 2        match: ["*Concurrent.*"],        c2: {            Exclude:true,        },    },]

編寫編譯器指令

您必須在指令文件中編寫編譯器指令。您可以對要寫入指令文件的每個編譯器指令重複以下步驟。

單個編譯器指令寫入指令文件中的指令塊內。請參閱編寫指令文件

  1. 插入以下代碼塊,作爲您可以使用的模板,以編寫單獨的編譯器指令。這個代碼塊是一個指令塊。

2. {3.         match: [],4.         c1: {5.             //c1 directive options6.         },7.         c2: {8.             //c2 directive options9.         },10.          //Directive options applicable to all compilers    },

例如:

match: ["java*.*", "oracle*.*"],

  • 爲該c1屬性提供一塊以逗號分隔的指令選項。確保這些選項對 c1 編譯器有效。

例如:

c1: {            Enable: true,            Exclude: true,            BreakAtExecute: true,        },

  • 爲該c2屬性提供一塊以逗號分隔的指令選項。此塊可以包含通用和 c2 專有編譯器選項的混合。

例如:

c2: {            Enable: false,            MaxNodeLimit: 1000,        },

  • 在指令的末尾提供您希望適用於所有編譯器的選項。這些選項被認爲是在公共塊的範圍內編寫的。選項以逗號分隔。

例如:

BreakAtCompile: true,DumpReplay: true,

  • 通過完成以下步驟清理文件。
    1. 檢查指令選項的重複。如果發生衝突,則最後一次出現的選項優先。衝突通常發生在公共塊和 c1 或 c2 塊之間,而不是發生在 c1 和 c2 塊之間。
    2. 避免在公共塊中編寫 c2 獨佔指令選項。儘管 common 塊可以接受 common 和 c2-exclusive 選項的混合,但以這種方式構造指令是沒有意義的,因爲 common 塊中的 c2-exclusive 選項對 c1 編譯器沒有影響。改爲在 c2 塊中寫入 c2 獨佔選項。
    3. 如果c1orc2屬性沒有相應的指令選項,則省略該編譯器的屬性值語法。

以下示例顯示了基於先前示例的結果指令是:

{        match: ["java*.*", "oracle*.*"],        c1: {            Enable: true,            Exclude: true,            BreakAtExecute: true,        },        c2: {            Enable: false,            MaxNodeLimit: 1000,        },        BreakAtCompile: true,        DumpReplay: true,    },

指令文件的 JSON 格式允許以下語法偏差:

  • 額外的尾隨逗號在數組和對象中是可選的。
  • 屬性是字符串,可以選擇放在引號內。
  • 如果數組只包含一個元素,則括號是可選的。

因此,以下示例顯示了一個有效的編譯器指令:

{       "match": "*Concurrent.*",        c2: {            "Exclude": true,        }    },

在編譯器指令中編寫方法模式

Accstr是一種方法模式,您可以精確編寫,也可以使用通配符進行概括。您可以指定哪些最匹配的 Java 代碼應該應用伴隨的指令選項,或者哪些 Java 代碼應該被內聯。

編寫方法模式:

  1. 使用以下語法來編寫您的方法模式:package/class.method(parameter_list). 要使用通配符概括方法模式,請參閱步驟 2。

以下示例顯示了使用此語法的方法模式:

java/lang/String.indexOf()

其他格式樣式也可用。這確保了與早期的方法匹配方式(例如 CompileCommand)的向後兼容性。上一個示例的有效格式選項包括:

  • java/lang/String.indexOf()
  • java/lang/String,indexOf()
  • java/lang/String indexOf()
  • lang.String::indexOf()

最後一種格式樣式與 HotSpot 輸出相匹配。

  1. *在要概括部分方法模式的位置插入通配符 ( )。

以下示例是步驟 1 中方法模式示例的有效概括:

  • java/lang/String.indexOf*
  • *lang/String.indexOf*
  • *va/lang*.*dex*
  • java/lang/String.*
  • *.*

泛化的增加會導致精度降低。更多的 Java 代碼成爲與方法模式的潛在匹配。因此,*明智地使用通配符 ( )很重要。

  1. 根據 Java 規範修改方法模式的簽名部分。簽名匹配必須準確,否則簽名默認爲通配符 ( *)。省略的簽名也默認爲通配符。簽名不能包含通配符。
  2. 可選:如果您編寫一個方法模式來伴隨inline指令選項,那麼您必須在方法模式前加上附加字符。請參閱編寫內聯指令選項

編寫內聯指令選項

inline指令選項的屬性需要一組帶有特殊命令前綴的方法模式。這表明哪些方法模式應該或不應該內聯。

  1. 寫入inline: 指令的公共塊、 c1 塊或 c2 塊。
  2. 添加一系列精心排序的方法模式。執行第一個匹配方法模式上的前綴命令。數組中剩餘的方法模式將被忽略。
  3. 前綴 a+以強制內聯任何匹配的 Java 代碼。
  4. 前綴 a-以防止內聯任何匹配的 Java 代碼。
  5. 可選:如果您需要將內聯行爲應用於多個方法模式,則重複步驟 1 到 4 以編寫多個inline語句。不要編寫包含多個方法模式的單個數組。

以下示例顯示了inline指令選項:

  • inline: ["+java/lang*.*", "-sun*.*"]
  • inline: "+java/lang*.*"

使用啓用選項防止重複

您可以使用該Enable選項來隱藏指令的各個方面並防止指令之間的重複。

在以下示例中,c1編譯器指令的屬性是相同的。:

[    {        match: ["java*.*"],        c1: {            BreakAtExecute: true,            BreakAtCompile: true,            DumpReplay: true,            DumpInline: true,        },        c2: {            MaxNodeLimit: 1000,        },    },    {        match: ["oracle*.*"],        c1: {            BreakAtExecute: true,            BreakAtCompile: true,            DumpReplay: true,            DumpInline: true,        },        c2: {            MaxNodeLimit: 2000,        },    },]

以下示例顯示瞭如何使用該Enable選項解決不需要的代碼重複。Enable隱藏塊指令並使它們無法匹配。

[    {        match: ["java*.*"],        c1: {            Enable: false,        },        c2: {            MaxNodeLimit: 1000,        },    },    {        match: ["oracle*.*"],        c1: {            Enable: false,        },        c2: {            MaxNodeLimit: 2000,        },    },    {        match: ["java*.*", "oracle*.*"],        c1: {            BreakAtExecute: true,            BreakAtCompile: true,            DumpReplay: true,            DumpInline: true,        },        c2: {            //Unreachable code        },    },]

通常,第一個匹配指令應用於方法的編譯。該Enable選項爲本規則提供了一個例外。通常c1在第一個或第二個指令中編譯的方法現在用c1第三個指令的塊編譯。的c2第三指令塊是不可訪問,因爲c2在第一和第二指令取優先級塊。

理解指令

什麼是默認指令?

default 指令是一個編譯器指令,它包含所有可能的指令選項的默認值。它是堆棧中最底層的指令,匹配提交編譯的每個方法。

當您設計新的編譯器指令時,您需要指定新指令與默認指令的不同之處。默認指令成爲指導您的設計決策的模板。

默認指令中的指令選項值

您可以打印一個空指令堆棧以顯示默認編譯器指令中所有指令選項的匹配條件和值:

Directive: (default) matching: *.* c1 directives:  inline: -  Enable:true Exclude:false BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:80000 c2 directives:  inline: -  Enable:true Exclude:false BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:80000

筆記:

某些選項僅適用於c2編譯器。有關完整列表,請參見表 2-2

新指令中的指令選項值

在新指令中,您必須指定指令與默認指令的不同之處。如果您未指定指令選項,則該選項會保留默認指令中的值。

例子:

 [    {        match: ["*Concurrent.*"],        c2: {            MaxNodeLimit: 1000,        },        Exclude:true,    },]

當您向指令堆棧中添加新指令時,默認指令將成爲堆棧中最底部的指令。請參閱指令堆棧中的指令如何排序?有關此過程的說明。對於此示例,當您打印指令堆棧時,它會顯示新指令中指定的指令選項與默認指令中的值有何不同:

Directive: matching: *Concurrent.* c1 directives:  inline: -  Enable:true Exclude:true BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:80000  c2 directives:  inline: -  Enable:true Exclude:true BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:1000 Directive: (default) matching: *.* c1 directives:  inline: -  Enable:true Exclude:false BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:80000  c2 directives:  inline: -  Enable:true Exclude:false BreakAtExecute:false BreakAtCompile:false Log:false PrintAssembly:false PrintInlining:false PrintNMethods:false BackgroundCompilation:true ReplayInline:false DumpReplay:false DumpInline:false CompilerDirectivesIgnoreCompileCommands:false DisableIntrinsic: BlockLayoutByFrequency:true PrintOptoAssembly:false PrintIntrinsics:false TraceOptoPipelining:false TraceOptoOutput:false TraceSpilling:false Vectorize:false VectorizeDebug:0 CloneMapDebug:false IGVPrintLevel:0 MaxNodeLimit:80000

指令如何應用於代碼?

指令基於方法匹配過程應用於代碼。提交編譯的每個方法都與指令堆棧中的指令匹配。

將方法與指令堆棧中的指令匹配的過程由 CompilerBroker 執行。

方法匹配過程

當一個方法被提交進行編譯時,該方法的完全限定名稱將與指令堆棧中的匹配條件進行比較。堆棧中匹配的第一個指令應用於該方法。堆棧中的其餘指令將被忽略。如果未找到匹配項,則應用默認指令。

對編譯中的所有方法重複此過程。一個編譯中可以應用多個指令,但每個方法只能應用一個指令。堆棧中的所有指令都被認爲是活動的,因爲它們可能適用。主動指令和應用指令之間的主要區別是:

  • 如果指令存在於指令堆棧中,則指令處於活動狀態。
  • 如果指令影響代碼,則應用指令。

示例 2-1 找到匹配項時

下面的例子展示了一個提交編譯的方法:

public int exampleMethod(int x){      return x;}

基於方法匹配標準,Directive 2從以下示例指令堆棧中應用:

Directive 2: matching: *.*example*Directive 1: matching: *.*exampleMethod*Directive 0: (default) matching: *.*

示例 2-2 未找到匹配項時

下面的例子展示了一個提交編譯的方法:

public int otherMethod(int y){      return y;}

根據方法匹配標準,Directive 0(默認指令)從以下示例指令堆棧中應用:

Directive 2: matching: *.*example*Directive 1: matching: *.*exampleMethod*Directive 0: (default) matching: *.*

編寫新指令的指南

  • 沒有提供反饋機制來驗證哪個指令應用於給定的方法。相反,使用 Java 管理擴展 (JMX) 等分析器來測量應用指令的累積效果。
  • CompilerBroker 忽略創建錯誤代碼的指令選項,例如在不提供支持的平臺上強制執行硬件指令。顯示警告消息。
  • 指令選項與典型的命令行標誌具有相同的限制。例如,僅當中間表示 (IR) 不會變得太大時,纔會遵循內聯代碼的說明。

編譯器控制和向後兼容性

CompileCommand 和命令行標誌可以與編譯器控制指令一起使用。

儘管 Compiler Control 可以替代 CompileCommand,但提供了向後兼容性。可以同時使用兩者。編譯器控制獲得優先權。根據以下優先級處理衝突:

  1. 編譯器控制
  2. 編譯命令
  3. 命令行標誌
  4. 默認值

示例 2-3 混合編譯器控制和編譯命令

以下列表顯示了少量編譯選項和值:

  • 編譯器控制:
    • Exclude: true
    • BreakAtExecute: false
  • 編譯命令:
    • BreakAtExecute: true
    • BreakAtCompile: true
  • 默認值:
    • Exclude: false
    • BreakAtExecute: false
    • BreakAtCompile: false
    • Log: false

對於本示例中的選項和值,生成的編譯是通過使用處理向後兼容性衝突的規則來確定的:

  • Exclude: true
  • BreakAtExecute: false
  • BreakAtCompile: true
  • Log: false

編譯器指令和命令行

您可以在啓動程序時使用命令行界面添加和打印編譯器指令。

您只能在命令行指定一個指令文件。該文件中的所有指令都添加到指令堆棧中,並在程序啓動時立即激活。在命令行添加指令使您能夠在程序的早期階段測試指令的性能影響。您還可以專注於調試和開發程序。

通過命令行添加指令

以下命令行選項指定指令文件:

XX:CompilerDirectivesFile=file

啓動 Java 程序時包括此命令行選項。以下示例顯示了此選項,其開頭爲TestProgram:

java -XX:+UnlockDiagnosticVMOptions -XX:CompilerDirectivesFile=File_A.json TestProgram

在示例中:

  • -XX:+UnlockDiagnosticVMOptions啓用診斷選項。在命令行中添加指令之前,您必須輸入它。
  • -XX:CompilerDirectivesFile是一種診斷選項。您可以使用它來指定一個指令文件以添加到指令堆棧中。
  • json是一個指令文件。該文件可以包含多個指令,所有這些指令都在程序啓動時添加到活動指令堆棧中。
  • 如果json包含語法錯誤或格式錯誤的指令,則會顯示錯誤消息並且TestProgram不會啓動。

通過命令行打印指令

當程序啓動或通過診斷命令添加附加指令時,您可以自動打印指令堆棧。以下命令行選項可啓用此行爲:

-XX:+CompilerDirectivesPrint

以下示例顯示如何在命令行中包含此診斷命令:

java -XX:+UnlockDiagnosticVMOptions -XX:+CompilerDirectivesPrint -XX:CompilerDirectivesFile=File_A.json TestProgram 

編譯器指令和診斷命令

您可以使用診斷命令來管理哪些指令在運行時處於活動狀態。您可以在不重新啓動正在運行的程序的情況下添加或刪除指令。

製作一個完美的指令文件可能需要一些迭代和實驗。診斷命令提供了強大的機制來測試指令堆棧中指令的不同配置。診斷命令使您無需重新啓動正在運行的程序的 JVM 即可添加或刪除指令。

獲取 Java 進程標識號

要測試指令,您必須找到正在運行的程序的處理器標識符 (PID) 編號。

  1. 打開一個終端。
  2. 輸入jcmd命令。

該jcmd命令返回正在運行的 Java 進程的列表以及它們的 PID 號。在以下示例中,返回的信息是TestProgram:

11084 TestProgram

通過診斷命令添加指令

您可以通過以下診斷命令將文件中的所有指令添加到指令堆棧中。

句法:

jcmd pid Compiler.directives_add file

以下示例顯示了診斷命令:

jcmd 11084 Compiler.directives_add File_B.json

終端報告添加的單個指令的數量。如果指令文件包含語法錯誤或格式錯誤的指令,則會顯示一條錯誤消息,並且不會將文件中的指令添加到堆棧中,並且不會對正在運行的程序進行任何更改。

通過診斷命令刪除指令

您可以使用診斷命令刪除指令。

要從指令堆棧中刪除最頂層的單個指令,請輸入:

jcmd pid Compiler.directives_remove

要清除添加到指令堆棧中的每個指令,請輸入:

jcmd pid Compiler.directives_clear

不可能指定要刪除的整個指令文件,也沒有任何其他方法可用於批量刪除指令。

通過診斷命令打印指令

您可以使用診斷命令打印正在運行的程序的指令堆棧。

要打印完整指令堆棧的詳細說明,請輸入:

jcmd pid Compiler.directives_print

示例輸出顯示在什麼是默認指令?

指令堆棧中的指令如何排序?

指令文件和指令中指令的順序非常重要。堆棧中最頂部、最匹配的指令獲得優先級並應用於代碼編譯。

以下示例說明了示例指令堆棧中指令文件的順序。示例中的指令文件包含以下指令:

  • File_A包含Directive 1和Directive 2。
  • File_B包含Directive 3.
  • File_C包含Directive 4和Directive 5。

使用或不使用指令啓動應用程序

您可以在TestProgram不指定指令文件的情況下啓動。

  • 要開始時TestProgram不添加任何指令,請在命令行中輸入以下命令:

java TestProgram

  • TestProgram在沒有指定任何指令文件的情況下啓動。
  • 默認指令始終是指令堆棧中最底部的指令。圖 2-1將默認指令顯示爲Directive 0. 當您不指定指令文件時,默認指令也是最頂層的指令,它獲得優先級。

圖 2-1 在沒有指令的情況下啓動程序


“圖 2-1 不使用指令啓動程序”的說明

您可以啓動應用程序並指定指令。

  • 要啓動TestProgram應用程序並將指令添加File_A.json到指令堆棧,請在命令行中輸入以下命令:

java -XX:+UnlockDiagnosticVMOptions -XX:CompilerDirectivesFile=File_A.json TestProgram

  • TestProgram開始並將指令File_A添加到堆棧中。指令文件中最頂層的指令成爲指令堆棧中最頂層的指令。
  • 圖 2-2顯示堆棧中指令的順序,從上到下,變爲 [1, 2, 0]。

圖 2-2 使用指令啓動程序


“圖 2-2 使用指令啓動程序”的說明

向正在運行的應用程序添加指令

您可以通過診斷命令向正在運行的應用程序添加指令。

  • 要將所有指令添加File_B到指令堆棧中,請輸入以下命令:

jcmd 11084 Compiler.directives_add File_B.json

in 指令File_B 被添加到棧頂。

  • 圖 2-3顯示指令在堆棧中的順序變爲 [3, 1, 2, 0]。

圖 2-3 向正在運行的程序添加指令


“圖2-3 向運行中的程序添加指令”的說明

您可以在TestProgram運行時通過診斷命令添加指令文件:

  • 要將所有指令添加File_C到指令堆棧,請輸入以下命令。

jcmd 11084 Compiler.directives_add File_C.json

  • 圖 2-4顯示指令在堆棧中的順序變爲 [4, 5, 3, 1, 2, 0]。

圖 2-4 向正在運行的程序添加多個指令


“圖2-4 給一個正在運行的程序添加多個指令”的說明

從指令堆棧中刪除指令

您可以通過診斷命令從指令堆棧中刪除最頂層的指令。

  • 要從Directive 4堆棧中刪除,請輸入以下命令:

jcmd 11084 Compiler.directives_remove

  • 要刪除更多內容,請重複此診斷命令,直到僅保留默認指令。您無法刪除默認指令。
  • 圖 2-5顯示指令在堆棧中的順序變爲 [5, 3, 1, 2, 0]。

圖 2-5 從堆棧中刪除一個指令


“圖2-5從堆棧中刪除一個指令”的說明

您可以從指令堆棧中刪除多個指令。

  • 要清除指令堆棧,請輸入以下命令:

jcmd 11084 Compiler.directives_clear

  • 除默認指令外,所有指令都被刪除。您無法刪除默認指令。
  • 圖 2-6顯示只Directive 0保留在堆棧中。

圖 2-6 從堆棧中刪除所有指令


“圖 2-6 從堆棧中刪除所有指令”的說明

類數據共享Class Data Sharing

本章介紹了類數據共享 (CDS) 功能,該功能有助於減少 Java 應用程序的啓動時間和內存佔用。

話題:

班級數據共享

重新生成共享存檔

手動控制類數據共享

班級數據共享

類數據共享 (CDS) 功能有助於減少多個 Java 虛擬機 (JVM) 之間的啓動時間和內存佔用。

從 JDK 12 開始,默認的 CDS 存檔與 Oracle JDK 二進制文件一起預先打包。默認 CDS 存檔是在 JDK 構建時通過運行創建的-Xshare:dump,使用 G1 GC 和 128M Java 堆。它使用內置時生成的默認類列表,其中包含選定的核心庫類。默認 CDS 存檔位於以下位置:

  • 在 Linux 和 macOS 平臺上,共享存檔存儲在/lib/[arch]/server/classes.jsa
  • 在 Windows 平臺上,共享存檔存儲在/bin/server/classes.jsa

默認情況下,默認 CDS 存檔在運行時啓用。指定-Xshare:off禁用默認共享存檔。請參閱重新生成共享存檔以創建自定義共享存檔。在創建和使用自定義共享存檔時,對轉儲時間和運行時使用相同的 Java 堆大小。

當 JVM 啓動時,共享存檔是內存映射的,以允許在多個 JVM 進程之間共享這些類的只讀 JVM 元數據。由於訪問共享存檔比加載類更快,因此減少了啓動時間。

ZGC、G1、串行和並行垃圾收集器支持類數據共享。共享 Java 堆對象特性(類數據共享的一部分)在 64 位非 Windows 平臺上僅支持 G1 垃圾收集器。

在 Java SE 中包含 CDS 的主要動機是減少啓動時間。應用程序相對於它使用的核心類的數量越小,節省的啓動時間部分就越大。

新 JVM 實例的佔用空間成本已通過兩種方式降低:

  1. 同一主機上的共享存檔的一部分被映射爲只讀並在多個 JVM 進程之間共享。否則,需要在每個 JVM 實例中複製這些數據,這會增加應用程序的啓動時間。
  2. 共享檔案以 Java Hotspot VM 使用的形式包含類數據。不使用訪問運行時模塊化映像中的原始類信息所需的內存。這些內存節省允許在同一系統上同時運行更多應用程序。在 Windows 應用程序中,進程的內存佔用(由各種工具衡量)可能會增加,因爲更多的頁面被映射到進程的地址空間。這種增加被保存運行時模塊化映像上的部分所需的內存量(在 Windows 內部)減少所抵消。減少足跡仍然是重中之重。

應用類-數據共享

爲了進一步減少啓動時間和佔用空間,引入了應用程序類數據共享 (AppCDS),它擴展了 CDS 以包括從應用程序類路徑中選擇的類。

此功能允許將應用程序類放置在共享驅動器中。公共類元數據在不同的 Java 進程之間共享。AppCDS 允許內置系統類加載器、內置平臺類加載器和自定義類加載器加載存檔類。當多個 JVM 共享同一個存檔文件時,可以節省內存並提高整體系統響應時間。

請參見應用類數據共享在Java開發工具包工具的規格。

動態 CDS 存檔

動態 CDS 歸檔擴展了應用程序類數據共享 (AppCDS),以允許在 Java 應用程序退出時動態歸檔類。

它通過消除爲每個應用程序創建類列表的試運行來簡化 AppCDS 的使用。歸檔的類包括默認 CDS 歸檔中不存在的所有加載的應用程序類和庫類。

要創建動態 CDS 存檔,請使用以下命令運行 Java 應用程序:

java -XX:ArchiveClassesAtExit=<dynamic archive> -cp <app jar> MyApp

動態CDS歸檔在Java開發工具包工具的規格。

重新生成共享存檔

您可以爲所有支持的平臺重新生成共享存檔。

與 JDK 一起安裝的默認類列表僅包含一小組核心庫類。您可能希望在共享存檔中包含其他類。要使用默認 CDS 存檔作爲基礎存檔創建動態 CDS 存檔,請在命令行中添加以下選項:

java -XX:ArchiveClassesAtExit=<dynamic archive>

在每個應用程序的默認系統之上創建一個單獨的動態生成的存檔。您可以將動態存檔的名稱指定爲-XX:ArchiveClassesAtExit選項的參數 。

要重新生成存檔文件,請以管理員身份登錄。在聯網情況下,登錄到與Java SE 安裝相同架構的計算機。確保您具有寫入安裝目錄的權限。

要使用用戶定義的類列表重新生成共享存檔,請輸入以下命令:

java -XX:SharedClassListFile=<class_list_file> -Xshare:dump

生成存檔時會打印診斷信息。

手動控制類數據共享

默認情況下啓用類數據共享。您可以手動啓用和禁用此功能。

您可以使用以下命令行選項進行診斷和調試。

-Xshare:off

禁用班級數據共享。

-Xshare:on

啓用班級數據共享。如果無法啓用類數據共享,則打印錯誤消息並退出。

筆記:

這-Xshare:on僅用於測試目的,可能會由於操作系統使用地址空間佈局隨機化而導致間歇性故障。不應在生產環境中使用此選項。

-Xshare:auto

默認啓用班級數據共享。儘可能啓用班級數據共享。

Java HotSpot 虛擬機性能增強Java HotSpot Virtual Machine Performance Enhancements

本章介紹了 Oracle 的 HotSpot 虛擬機技術中的性能增強。

話題:

Compact Strings 壓實字符串

緊湊字符串功能爲字符串引入了一種節省空間的內部表示。

來自不同應用程序的數據表明,字符串是 Java 堆使用的主要組成部分,並且大多數java.lang.String對象僅包含 Latin-1 字符。這樣的字符只需要一個字節的存儲空間。結果,java.lang.String對象的內部字符數組中的一半空間未被使用。Java SE 9 中引入的壓縮字符串功能減少了內存佔用,並減少了垃圾收集活動。如果您在應用程序中觀察到性能迴歸問題,則可以禁用此功能。

壓縮字符串功能不會引入新的公共 API 或接口。它將java.lang.String類的內部表示從 UTF-16(兩個字節)字符數組修改爲帶有附加字段以標識字符編碼的字節數組。其他字符串相關的類,如AbstractStringBuilder,StringBuilder和StringBuffer更新使用類似的內部表示。

在 Java SE 9 中,默認啓用壓縮字符串功能。因此,java.lang.String該類將字符存儲爲每個字符的一個字節,編碼爲 Latin-1。附加字符編碼字段指示使用的編碼。HotSpot VM 字符串內部函數已更新和優化以支持內部表示。

您可以通過-XX:-CompactStrings在java命令行中使用該標誌來禁用壓縮字符串功能。當該功能被禁用時,java.lang.String該類將字符存儲爲兩個字節,編碼爲 UTF-16,HotSpot VM 字符串內部函數使用 UTF-16 編碼。

分層編譯

Java SE 7 中引入的分層編譯爲服務器 VM 帶來了客戶端 VM 的啓動速度。無需繁瑣的編譯,服務器 VM 使用解釋器收集有關發送到編譯器的方法的分析信息。通過分層編譯,服務器 VM 還使用客戶端編譯器生成方法的編譯版本,這些方法收集有關自身的分析信息。編譯後的代碼比解釋器快得多,並且程序在分析階段以更高的性能執行。通常,啓動速度比客戶端 VM 啓動速度快,因爲服務器編譯器生成的最終代碼可能在應用程序初始化的早期階段可用。分層編譯還可以獲得比普通服務器 VM 更好的峯值性能,因爲,

服務器虛擬機默認啓用分層編譯。支持 64 位模式和壓縮普通對象指針。您可以通過-XX:-TieredCompilation在java命令中使用標誌來禁用分層編譯。

爲了容納使用分層編譯生成的額外分析代碼,代碼緩存的默認大小乘以 5 倍。爲了有效地組織和管理更大的空間,使用了分段代碼緩存

分段代碼緩存

代碼緩存是 Java 虛擬機存儲生成的本機代碼native code的內存區域。它被組織爲位於連續內存塊之上的單個堆數據結構。 

代碼緩存不是隻有一個代碼堆,而是分成多個段,每個段包含特定類型的編譯代碼。這種分段可以更好地控制JVM內存佔用,縮短編譯方法的掃描時間,顯着減少代碼緩存的碎片,提高性能。

代碼緩存分爲以下三段:

表 5-1 分段代碼緩存

代碼緩存段

描述

JVM 命令行參數

非方法

此代碼堆包含非方法代碼,例如編譯器緩衝區和字節碼解釋器。這種代碼類型永遠留在代碼緩存中。代碼堆的大小固定爲 3 MB,剩餘的代碼緩存均勻分佈在已分析和未分析的代碼堆中。

-XX:NonMethodCodeHeapSize

異形Profiled

此代碼堆包含生命週期較短的輕度優化、概要分析的方法。

–XX:ProfiledCodeHeapSize

非異形Non-profiled

此代碼堆包含完全優化的、未分析的方法,其生命週期可能很長。

-XX:NonProfiledCodeHeapSize

壓縮普通對象指針Compressed Ordinary Object Pointer

Java Hotspot 中的普通對象指針 (oop) 是指向對象的託管指針。通常,oop 與本地機器指針的大小相同,在 LP64 系統上爲 64 位。在 ILP32 系統上,最大堆大小小於 4 GB,這對於許多應用程序來說是不夠的。在 LP64 系統上,給定程序使用的堆可能必須比在 ILP32 系統上運行時大 1.5 倍左右。此要求是由於託管指針的擴展大小所致。內存很便宜,但現在帶寬和緩存供不應求 in short supply,因此顯着增加堆的大小並且僅超過 4 GB 的限制是不可取的。

Java 堆中的託管指針指向在 8 字節地址邊界上對齊aligned on 8-byte address boundaries的對象。壓縮 oops 將託管指針(在 Java 虛擬機 (JVM) 軟件中的許多但不是所有地方)表示爲 64 位 Java 堆基址的 32 位對象偏移量。因爲它們是對象偏移量而不是字節偏移量,所以 oops 可用於尋址多達 40 億個對象(不是字節),或高達約 32 GB 的堆大小。要使用它們,必須將它們縮放 8 倍並添加到 Java 堆基地址中以找到它們所引用的對象。使用壓縮 oops 的對象大小與 ILP32 模式中的對象大小相當。

術語解碼是指將 32 位壓縮 oop 轉換爲 64 位本機地址並添加到託管堆中的操作 a 32-bit compressed oop is converted to a 64-bit native address 。術語編碼指的是逆操作。

Java SE 6u23 及更高版本默認支持並啓用壓縮 oops。在 Java SE 7 中,默認情況下爲 64 位 JVM 進程啓用壓縮 oops,當-Xmx未指定並且值-Xmx小於 32 GB 時compressed oops is enabled by default for 64-bit JVM processes when -Xmx isn't specified and for values of -Xmx less than 32 gigabytes.。對於早於 6u23 版本的 JDK 版本-XX:+UseCompressedOops,在java命令中使用該標誌以啓用壓縮的 oops。

基於零的壓縮普通對象指針Zero-Based Compressed Ordinary Object Pointers

當 JVM 在 64 位 JVM 進程中使用壓縮的普通對象指針 (oops) 時,JVM 軟件會向操作系統發送請求,爲從虛擬地址 0 開始的 Java 堆保留內存the JVM software sends a request to the operating system to reserve memory for the Java heap starting at virtual address zero. 。如果操作系統支持這樣的請求並且可以在虛擬地址零處爲 Java 堆保留內存,則使用基於零的壓縮 oops。then zero-based compressed oops are used

當使用基於零的壓縮 oops 時,可以從 32 位對象偏移量解碼 64 位指針,而不包括 Java 堆基地址。對於小於 4 GB 的堆大小,JVM 軟件可以使用字節偏移量而不是對象偏移量,從而也避免將偏移量縮放 8。 thus also avoid scaling the offset by 8將 64 位地址編碼爲 32 位偏移量相對有效。Encoding a 64-bit address into a 32-bit offset is correspondingly efficient.

對於高達 26 GB 的 Java 堆大小,Linux 和 Windows 操作系統通常可以在虛擬地址零分配 Java 堆。

逃逸分析

逃逸分析是一種技術,Java HotSpot 服務器編譯器可以通過它分析新對象的使用範圍並決定是否在 Java 堆上分配對象。

Java SE 6u23 及更高版本默認支持並啓用轉義分析。

Java HotSpot Server Compiler 實現了以下描述的對流不敏感的轉義分析算法: flow-insensitive escape analysis  algorithm

 [Choi99] Jong-Deok Choi, Manish Gupta, Mauricio Seffano,          Vugranam C. Sreedhar, Sam Midkiff,          "Escape Analysis for Java", Procedings of ACM SIGPLAN          OOPSLA  Conference, November 1, 1999

根據逃逸分析,對象的逃逸狀態可以是以下狀態之一:

  • GlobalEscape: 對象轉義方法和線程。The object escapes the method and thread.例如,對象存儲在靜態字段中,存儲在轉義對象的字段中,或作爲當前方法的結果返回。
  • ArgEscape: 該對象作爲參數傳遞或由參數引用,但在調用期間不會全局轉義。The object is passed as an argument or referenced by an argument but does not globally escape during a call.這種狀態是通過分析被調用方法的字節碼來確定的。
  • NoEscape: 該對象是一個標量可替換對象,這意味着它的分配可以從生成的代碼中刪除。The object is a scalar replaceable object, which means that its allocation could be removed from generated code.

在逃逸分析之後,服務器編譯器從生成的代碼中消除標量可替換對象分配和相關聯的鎖。 the server compiler eliminates the scalar replaceable object allocations and the associated locks from generated code.服務器編譯器還消除了對不會全局轉義的對象的鎖定。The server compiler also eliminates locks for objects that do not globally escape. 對於一個全局不逃逸對象,它並不能用棧分配代替堆分配。It does not replace a heap allocation with a stack allocation for objects that do not globally escape.

以下示例描述了逃逸分析的一些場景:

  • 服務器編譯器可能會消除某些對象分配。例如,一個方法創建一個對象的防禦性副本並將副本返回給調用者。

public class Person {  private String name;  private int age;  public Person(String personName, int personAge) {    name = personName;    age = personAge;  }public Person(Person p) { this(p.getName(), p.getAge()); }  public int getName() { return name; }  public int getAge() { return age; }} public class Employee {  private Person person;  // makes a defensive copy to protect against modifications by caller        public Person getPerson() { return new Person(person) };                public void printEmployeeDetail(Employee emp) {          Person person = emp.getPerson();          // this caller does not modify the object, so defensive copy was unnecessary                System.out.println ("Employee's name: " + person.getName() + "; age: "  + person.getAge());             }}      

該方法進行復制以防止調用者修改原始對象。如果編譯器確定getPerson正在循環中調用該方法,則編譯器內聯該方法。通過使用轉義分析,當編譯器確定原始對象從未被修改時,編譯器可以優化並消除進行復制的調用。By using escape analysis, when the compiler determines that the original object is never modified, the compiler can optimize and eliminate the call to make a copy.

  • 如果服務器編譯器確定一個對象是線程本地的,它可能會消除同步塊(鎖省略)(lock elision) 。例如,諸如StringBuffer和之類的方法Vector是同步的,因爲它們可以被不同的線程訪問。但是,在大多數情況下,它們以線程本地方式使用。they are used in a thread local manner.在使用線程本地的情況下,編譯器可以優化和刪除同步塊。the compiler can optimize and remove the synchronization blocks.

JVM 常量 API

JVM 常量 API 在包中定義 java.lang.constants,其中包含各種類型的可加載常量的標稱描述符。這些名義上的描述符對於操作類文件和編譯時或鏈接時程序分析工具的應用程序很有用。These nominal descriptors are useful for applications that manipulate class files and compile-time or link-time program analysis tools.

名義描述符不是可加載常量的值,而是對其值的描述,可以在給定類加載上下文的情況下重構。A nominal descriptor is not the value of a loadable constant but a description of its value, which can be reconstituted given a class loading context. 可加載常量是一個常量池條目,可以推送到操作數堆棧上,也可以出現在invokedynamic 指令的引導方法的靜態參數列表中。A loadable constant is a constant pool entry that can be pushed onto the operand stack or can appear in the static argument list of a bootstrap method for the invokedynamic instruction. 操作數棧是 JVM 指令獲取輸入和存儲輸出的地方。The operand stack is where JVM instructions get their input and store their output. 每個 Java 類文件都有一個常量池,其中包含多種常量,從編譯時已知的數字文字到必須在運行時解析的方法和字段引用。Every Java class file has a constant pool, which contains several kinds of constants, ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time.

使用非名義可加載常量non-nominal loadable constants(例如Class,其引用在運行時解析的 對象)的問題在於,這些引用取決於類加載上下文的正確性和一致性 the correctness and consistency of the class loading context。The issue with working with non-nominal loadable constants, such as a Class objects, whose references are resolved at run-time, is that these references depend on the correctness and consistency of the class loading context. 類加載可能會產生副作用,Class loading may have side effects, 例如運行您不想運行的代碼並拋出與訪問相關的異常和內存不足異常, such as running code that you don't want run and throwing access-related and out-of-memory exceptions, 您可以使用名義描述來避免這些異常。此外,類加載可能根本不可能。which you can avoid with nominal descriptions. In addition, class loading may not be possible at all.

請參閱包裝java.lang.constant

對非 Java 語言的支持Support for Non-Java Languages

本章介紹 Java 虛擬機中的非 Java 語言功能。

話題:

非 Java 語言特性介紹Introduction to Non-Java Language Features

Java 平臺標準版 (Java SE) 支持開發具有以下功能的應用程序:

  • 它們可以編寫一次並在任何地方運行
  • 由於 Java 沙箱安全模型,它們可以安全運行
  • 它們易於包裝和交付They are easy to package and deliver

Java SE 平臺在以下方面提供了強大的支持:

  • 併發
  • 垃圾收集
  • 對類和對象的反射訪問
  • JVM 工具接口 (JVM TI):供工具使用的本機編程接口。它提供了一種檢查狀態和控制在 JVM 中運行的應用程序執行的方法。JVM Tool Interface (JVM TI): A native programming interface for use by tools. It provides both a way to inspect the state and to control the execution of applications running in the JVM.

Oracle 的 HotSpot JVM 提供以下工具和特性:

  • DTrace:一種動態跟蹤實用程序,用於監視應用程序和操作系統的行爲。
  • 性能優化Performance optimizations
  • PrintAssembly:一個 Java HotSpot 選項,用於打印字節編碼和本機方法的彙編代碼。 A Java HotSpot option that prints assembly code for bytecoded and native methods.

Java SE 7 平臺使非 Java 語言能夠使用 JVM 的基礎架構和潛在的性能優化。The Java SE 7 platform enables non-Java languages to use the infrastructure and potential performance optimizations of the JVM.關鍵機制是invokedynamic指令,它簡化了 JVM 上動態類型語言的編譯器和運行時系統的實現。

靜態和動態類型Static and Dynamic Typing

如果編程語言在編譯時執行類型檢查,則它是靜態類型的。A programming language is statically-typed if it performs type checking at compile time. 類型檢查是驗證程序類型安全的過程。如果一個程序的所有操作的參數都是正確的類型,那麼它就是類型安全的。 Type checking is the process of verifying that a program is type safe. A program is type safe if the arguments of all of its operations are the correct type.

Java 是一種靜態類型語言。編譯程序時,類型信息可用於類和實例變量、方法參數、返回值和其他變量。Type information is available for class and instance variables, method parameters, return values, and other variables when a program is compiled.  Java 編程語言的編譯器使用此類型信息生成強類型字節碼,然後 JVM 可以在運行時有效地執行這些字節碼。The compiler for the Java programming language uses this type information to produce strongly typed bytecode, which can then be efficiently executed by the JVM at runtime.

以下 Hello World 程序示例演示了靜態類型demonstrates static typing。類型以粗體顯示

import java.util.Date;public class HelloWorld {    public static void main(String[] argv) {        String hello = "Hello ";        Date currDate = new Date();        for (String a : argv) {            System.out.println(hello + a);            System.out.println("Today's date is: " + currDate);        }    }}

如果編程語言在運行時執行類型檢查,則它是動態類型的。A programming language is dynamically-typed if it performs type checking at runtime.  JavaScript 和 Ruby 是動態類型語言的例子。這些語言在運行時而不是在編譯時驗證應用程序中的值是否符合預期類型。These languages verify at runtime, rather than at compile time, that values in an application conform to expected types. 通常,在編譯應用程序時,這些語言的類型信息不可用。Typically, type information for these languages is not available when an application is compiled. 對象的類型僅在運行時確定。過去,很難在 JVM 上高效地實現動態類型語言。In the past, it was difficult to efficiently implement dynamically-typed languages on the JVM.

以下是用 Ruby 編程語言編寫的 Hello World 程序示例:

#!/usr/bin/env rubyrequire 'date'hello = "Hello "currDate = DateTime.nowARGV.each do|a|  puts hello + a  puts "Date and time: " + currDate.to_send

在這個例子中,每個名字都是在沒有類型聲明的情況下引入的。In the example, every name is introduced without a type declaration. 主程序不位於持有者類型(Java 類HelloWorld)內。Java for循環的 Ruby 等效項位於動態類型ARGV變量內。The Ruby equivalent of the Java for loop is inside the dynamic type ARGV variable.循環體包含在一個稱爲閉包的塊中,這是動態語言中的一個常見特徵。The body of the loop is contained in a block called a closure, which is a common feature in dynamic languages.

靜態類型語言不一定是強類型語言Statically-Typed Languages Are Not Necessarily Strongly-Typed Languages

靜態類型的編程語言可以使用強類型或弱類型。Statically-typed programming languages can employ strong typing or weak typing. 使用強類型的編程語言對提供給其操作的值的類型進行了限制,A programming language that employs strong typing specifies restrictions on the types of values supplied to its operations, 如果其參數類型錯誤,它會阻止操作的執行。it prevents the execution of an operation if its arguments have the wrong type. 如果這些參數具有錯誤或不兼容的類型,則使用弱類型的語言將隱式轉換(或強制轉換)操作的參數。A language that employs weak typing would implicitly convert (or cast) arguments of an operation if those arguments have the wrong or incompatible types.

動態類型語言可以使用強類型或弱類型。例如,Ruby 編程語言是動態類型和強類型的。當使用某種類型的值初始化變量時,Ruby 編程語言不會將變量隱式轉換爲另一種數據類型。When a variable is initialized with a value of some type, the Ruby programming language does not implicitly convert the variable into another data type.

在以下示例中,Ruby 編程語言不會將具有Fixnum類型的數字 2 隱式轉換爲字符串。

a = "40"b = a + 2

編譯動態類型語言的挑戰The Challenge of Compiling Dynamically-Typed Languages

考慮以下動態類型方法 ,addtwo它將任意兩個數字(可以是任何數字類型)相加並返回它們的總和:

def addtwo(a, b)       a + b;end

假設您的組織正在addtwo爲編寫方法的編程語言實現編譯器和運行時系統。Suppose your organization is implementing a compiler and runtime system for the programming language in which the method addtwo is written.在強類型語言中,無論是靜態類型還是動態類型,+(加法運算符)的行爲取決於操作數類型。In a strongly-typed language, whether typed statically or dynamically, the behavior of + (the addition operator) depends on the operand types.一種靜態類型語言的編譯器選擇適當的執行+基於靜態類型的a和b。A compiler for a statically-typed language chooses the appropriate implementation of + based on the static types of a and b. 例如,如果a 和 b的類型是int,則Java 編譯器+使用iadd JVM 指令實現。 a Java compiler implements + with the iadd JVM instruction if the types of a and b are int. 加法運算符被編譯爲方法調用,因爲 JVM指令iadd要求操作數類型是靜態已知的。 The addition operator is compiled to a method call because the JVM iadd instruction requires the operand types to be statically known.

動態類型語言的編譯器必須將選擇推遲到運行時。A compiler for a dynamically-typed language must defer the choice until runtime. 該語句a + b被編譯爲方法調用+(a, b),其中+是方法名稱。The statement a + b is compiled as the method call +(a, b), where + is the method name. +JVM 中允許命名的方法,但 Java 編程語言中不允許。A method named + is permitted in the JVM but not in the Java programming language.如果動態類型語言的運行時系統能夠識別它a並且b是整數類型的變量,那麼運行時系統更願意調用+專用於整數類型而不是任意對象類型的實現。If the runtime system for the dynamically-typed language is able to identify that a and b are variables of integer type, then the runtime system would prefer to call an implementation of + that is specialized for integer types rather than arbitrary object types.

編譯動態類型語言的挑戰是如何實現一個運行時系統,該系統可以在程序編譯後選擇最合適的方法或函數實現。The challenge of compiling dynamically-typed languages is how to implement a runtime system that can choose the most appropriate implementation of a method or function — after the program has been compiled. 將所有變量視爲Object類型對象將無法有效工作;在Object類不包含一個名爲方法+。Treating all variables as objects of Object type would not work efficiently; the Object class does not contain a method named +.

在 Java SE 7 及更高版本中,該invokedynamic指令使運行時系統能夠自定義調用站點和方法實現之間的鏈接。In Java SE 7 and later, the invokedynamic instruction enables the runtime system to customize the linkage between a call site and a method implementation.在本例中,invokedynamic調用站點是+。the invokedynamic call site is +. invokedynamic通話網站是由一個手段相聯繫的方法引導方法,這是由編譯器是由JVM執行一次該網站鏈接的動態類型語言中規定的方法。An invokedynamic call site is linked to a method by means of a bootstrap method, which is a method specified by the compiler for the dynamically-typed language that is called once by the JVM to link the site. 假設編譯器發出一條invokedynamic調用的指令+,並假設運行時系統知道該方法adder(Integer,Integer),則運行時可以將invokedynamic調用點鏈接到該adder方法,如下所示:Assuming the compiler emitted an invokedynamic instruction that invokes +, and assuming that the runtime system knows about the method adder(Integer,Integer), the runtime can link the invokedynamic call site to the adder method as follows:

IntegerOps.java

class IntegerOps {  public static Integer adder(Integer x, Integer y) {    return x + y;  }}

例子.java

import java.util.*;import java.lang.invoke.*;import static java.lang.invoke.MethodType.*;import static java.lang.invoke.MethodHandles.*;class Example {  public static CallSite mybsm(    MethodHandles.Lookup callerClass, String dynMethodName, MethodType dynMethodType)    throws Throwable {    MethodHandle mh =      callerClass.findStatic(        Example.class,        "IntegerOps.adder",        MethodType.methodType(Integer.class, Integer.class, Integer.class));    if (!dynMethodType.equals(mh.type())) {      mh = mh.asType(dynMethodType);    }    return new ConstantCallSite(mh);  }}

在此示例中,IntegerOps該類屬於動態類型語言的運行時系統隨附的庫。In this example, the IntegerOps class belongs to the library that accompanies runtime system for the dynamically-typed language.

該Example.mybsm方法是將invokedynamic調用站點鏈接到該adder方法的引導程序方法。The Example.mybsm method is a bootstrap method that links the invokedynamic call site to the adder method.

callerClass對象是一個lookup對象,它是用於創建方法處理的工廠。The callerClass object is a lookup object, which is a factory for creating method handles.

MethodHandles.Lookup.findStatic方法(從被叫callerClass lookup對象)來創建用於該方法的靜態方法手柄adder。The MethodHandles.Lookup.findStatic method (called from the callerClass lookup object) creates a static method handle for the method adder.

注意:此引導程序方法invokedynamic僅將調用站點鏈接到該adder方法中定義的代碼。Note: This bootstrap method links an invokedynamic call site to only the code that is defined in the adder method. 它假定提供給invokedynamic調用站點的參數是Integer對象。It assumes that the arguments given to the invokedynamic call site are Integer objects. 如果引導程序方法invokedynamic的參數(在本例中爲callerClass、dynMethodName ,dynMethodType)發生變化,引導程序方法需要額外的代碼才能正確地將調用站點鏈接到要執行的適當代碼。 A bootstrap method requires additional code to properly link invokedynamic call sites to the appropriate code to execute if the parameters of the bootstrap method (in this example, callerClass, dynMethodName, and dynMethodType) vary.

在java.lang.invoke.MethodHandles類和java.lang.invoke.MethodHandle類包含創建基於現有的方法處理方法處理的各種方法。The java.lang.invoke.MethodHandles class and java.lang.invoke.MethodHandle class contain various methods that create method handles based on existing method handles.  asType如果mh方法句柄的方法類型與dynMethodType參數指定的方法類型不匹配,則此示例調用該方法。This example calls the asType method if the method type of the mh method handle does not match the method type specified by the dynMethodType parameter. 這使 bootstrap 方法能夠將invokedynamic調用站點鏈接到方法類型不完全匹配的 Java 方法。This enables the bootstrap method to link invokedynamic call sites to Java methods whose method types don’t exactly match.

ConstantCallSite bootstrap 方法返回的實例表示與不同invokedynamic指令相關聯的調用站點。The ConstantCallSite instance returned by the bootstrap method represents a call site to be associated with a distinct invokedynamic instruction.  ConstantCallSite實例的目標是永久的,永遠不能改變。The target for a ConstantCallSite instance is permanent and can never be changed.在這種情況下,一個 Java 方法adder是執行調用站點的候選方法。In this case, one Java method, adder, is a candidate for executing the call site. 此方法不必是 Java 方法。This method does not have to be a Java method. 相反,如果運行時系統可以使用多個這樣的方法,每個方法處理不同的參數類型,mybsm引導方法可以根據dynMethodType參數動態選擇正確的方法。Instead, if several such methods are available to the runtime system, each handling different argument types, the mybsm bootstrap method could dynamically select the correct method based on the dynMethodType argument.

調用動態指令The invokedynamic Instruction

您可以invokedynamic在 JVM 上動態類型語言的編譯器和運行時系統的實現中使用該指令。You can use the invokedynamic instruction in implementations of compilers and runtime systems for dynamically typed languages on the JVM.該invokedynamic指令使語言實現者能夠定義自定義鏈接。The invokedynamic instruction enables the language implementer to define custom linkage. 這與其他 JVM 指令(例如 )形成對比invokevirtual,其中特定於 Java 類和接口的鏈接行爲由 JVM 硬連接。This contrasts with other JVM instructions such as invokevirtual, in which linkage behavior specific to Java classes and interfaces is hard-wired by the JVM.

invokedynamic指令的每個實例稱爲動態調用點。Each instance of an invokedynamic instruction is called a dynamic call site. 創建動態調用站點的實例時,它處於未鏈接狀態,沒有爲調用站點指定要調用的方法。When an instance of the dynamic call site is created, it is in an unlinked state, with no method specified for the call site to invoke. 動態調用站點通過引導方法鏈接到方法。The dynamic call site is linked to a method by means of a bootstrap method.動態調用站點的引導程序方法是編譯器爲動態類型語言指定的方法。A dynamic call site's bootstrap method is a method specified by the compiler for the dynamically-typed language. 該方法由 JVM 調用一次以鏈接站點。從引導方法返回的對象永久確定調用站點的活動。The method is called once by the JVM to link the site. The object returned from the bootstrap method permanently determines the call site's activity.

該invokedynamic指令包含一個常量池索引(與其他invoke指令的格式相同)。The invokedynamic instruction contains a constant pool index (in the same format as for the other invoke instructions). 此常量池索引引用一個CONSTANT_InvokeDynamic條目。This constant pool index references a CONSTANT_InvokeDynamic entry. 此條目指定引導程序方法(CONSTANT_MethodHandle條目)、動態鏈接方法的名稱以及對動態鏈接方法的調用的參數類型和返回類型。 This entry specifies the bootstrap method (a CONSTANT_MethodHandle entry), the name of the dynamically-linked method, and the argument types and return type of the call to the dynamically-linked method.

在下面的示例中,運行時系統使用引導方法Example.mybsm將invokedynamic指令指定的動態調用點(即+加法運算符)鏈接到方法IntegerOps.adder。該方法adder和方法mybsm在編譯動態類型語言的挑戰中定義(爲清晰起見,已添加換行符)In the following example, the runtime system links the dynamic call site specified by the invokedynamic instruction (which is +, the addition operator) to the IntegerOps.adder method by using the Example.mybsm bootstrap method. The adder method and mybsm method are defined in The Challenge of Compiling Dynamically Typed Languages (line breaks have been added for clarity):: 

invokedynamic   InvokeDynamic  REF_invokeStatic:    Example.mybsm:      "(Ljava/lang/invoke/MethodHandles/Lookup;        Ljava/lang/String;        Ljava/lang/invoke/MethodType;)      Ljava/lang/invoke/CallSite;":    +:      "(Ljava/lang/Integer;        Ljava/lang/Integer;)      Ljava/lang/Integer;";

筆記:

字節碼示例使用ASM Java 字節碼操作和分析框架的語法。The bytecode examples use the syntax of the ASM Java bytecode manipulation and analysis framework.

使用invokedynamic指令調用動態鏈接方法包括以下步驟:

  1. 定義 Bootstrap 方法
  1. 指定常量池條目Specifying Constant Pool Entries
  1. 使用invokedynamic說明

定義 Bootstrap 方法

在運行時,JVM 第一次遇到invokedynamic指令時,它會調用 bootstrap 方法。At runtime, the first time the JVM encounters an invokedynamic instruction, it calls the bootstrap method.該方法將invokedynamic指令指定的名稱與執行目標方法的代碼聯繫起來,該方法由方法句柄引用。This method links the name that the invokedynamic instruction specifies with the code to execute the target method, which is referenced by a method handle. JVM 下次執行相同invokedynamic指令時,不會調用 bootstrap 方法;它會自動調用鏈接的方法句柄。The next time the JVM executes the same invokedynamic instruction, it does not call the bootstrap method; it automatically calls the linked method handle.

bootstrap 方法的返回類型必須是java.lang.invoke.CallSite. 該CallSite對象表示invokedynamic指令的鏈接狀態和它鏈接到的方法句柄。The bootstrap method's return type must be java.lang.invoke.CallSite. The CallSite object represents the linked state of the invokedynamic instruction and the method handle to which it is linked.

bootstrap 方法採用以下三個或更多參數:The bootstrap method takes three or more of the following parameters:

  • Lookup對象:用於在invokedynamic指令上下文中創建方法句柄的工廠。MethodHandles.Lookup object: A factory for creating method handles in the context of the invokedynamic instruction.
  • Stringobject:動態調用站點中提到的方法名稱。String object: The method name mentioned in the dynamic call site.
  • MethodTypeobject:動態調用站點的解析類型簽名。MethodType object: The resolved type signature of the dynamic call site.
  • invokedynamic指令的一個或多個附加靜態參數:從常量池中提取的可選參數旨在幫助語言實現者安全且緊湊地編碼對引導方法有用的附加元數據。原則上,名稱和額外參數是多餘的,因爲每個調用站點都可以被賦予自己獨特的引導方法。但是,這樣的做法很可能會產生很大的類文件和常量池One or more additional static arguments to the invokedynamic instruction: Optional arguments, drawn from the constant pool, are intended to help language implementers safely and compactly encode additional metadata useful to the bootstrap method. In principle, the name and extra arguments are redundant because each call site could be given its own unique bootstrap method. However, such a practice is likely to produce large class files and constant pools.

有關引導程序方法的示例,請參閱編譯動態類型語言的挑戰

指定常量池條目Specifying Constant Pool Entries

該invokedynamic指令包含對常量池中帶有CONSTANT_InvokeDynamic標記的條目的引用。The invokedynamic instruction contains a reference to an entry in the constant pool with the CONSTANT_InvokeDynamic tag. 該條目包含對常量池中其他條目的引用和對屬性的引用。 This entry contains references to other entries in the constant pool and references to attributes.看java.lang.invoke package documentation和Java 虛擬機規範。

示例常量池

以下示例顯示了類的常量池的摘錄Example,其中包含Example.mybsm將方法+與 Java 方法鏈接的引導方法adder:The following example shows an excerpt from the constant pool for the class Example, which contains the bootstrap method Example.mybsm that links the method + with the Java method adder:

    class #159; // #47    Utf8 "adder"; // #83    Utf8 "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;"; // #84    Utf8 "mybsm"; // #87    Utf8 "(Ljava/lang/invoke/MethodHandles/Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)      java/lang/invoke/CallSite;"; // #88    Utf8 "Example"; // #159    Utf8 "+"; // #166    // ...    NameAndType #83 #84; // #228    Method #47 #228; // #229    MethodHandle 6b #229; // #230    NameAndType #87 #88; // #231    Method #47 #231; // #232    MethodHandle 6b #232; // #233    NameAndType #166 #84; // #234    Utf8 "BootstrapMethods"; // #235    InvokeDynamic 0s #234; // #236

invokedynamic本示例中指令的常量池條目包含以下值:

  • CONSTANT_InvokeDynamic 標籤
  • 無符號缺值0 Unsigned short of value 0
  • 常量池索引#234。

值0是指存儲在BootstrapMethods屬性中的說明符數組中的第一個引導方法說明符。Bootstrap 方法說明符不在常量池表中。The value, 0, refers to the first bootstrap method specifier in the array of specifiers that are stored in the BootstrapMethods attribute. Bootstrap method specifiers are not in the constant pool table.它們包含在這個單獨的說明符數組中。每個 bootstrap 方法說明符都包含一個指向CONSTANT_MethodHandle常量池條目的索引,它是 bootstrap 方法本身。They are contained in this separate array of specifiers. Each bootstrap method specifier contains an index to a CONSTANT_MethodHandle constant pool entry, which is the bootstrap method itself.

以下示例顯示了來自同一常量池的摘錄BootstrapMethods,其中顯示了包含引導程序方法說明符數組的屬性:The following example shows an excerpt from the same constant pool that shows the BootstrapMethods attribute, which contains the array of bootstrap method specifiers:

  [3] { // Attributes    // ...    Attr(#235, 6) { // BootstrapMethods at 0x0F63      [1] { // bootstrap_methods        {  //  bootstrap_method          #233; // bootstrap_method_ref          [0] { // bootstrap_arguments          }  //  bootstrap_arguments        }  //  bootstrap_method      }    } // end BootstrapMethods  } // Attributes

bootstrap 方法mybsm方法句柄的常量池條目包含以下值:

  • CONSTANT_MethodHandle 標籤
  • 無符號字節值6 Unsigned byte of value 6
  • 常量池索引#232。

值6是REF_invokeStatic子標籤。有關此子標籤的更多信息,請參閱使用調用動態指令

使用invokedynamic指令Using the invokedynamic Instruction

以下示例顯示字節碼如何使用invokedynamic指令調用mybsm引導程序方法,該方法將動態調用站點(+,加法運算符)鏈接到該adder方法。此示例使用該+方法添加數字40和2(爲清晰起見,已添加換行符):The following example shows how the bytecode uses the invokedynamic instruction to call the mybsm bootstrap method, which links the dynamic call site (+, the addition operator) to the adder method. This example uses the + method to add the numbers 40 and 2 (line breaks have been added for clarity):

bipush  40;invokestatic    Method java/lang/Integer.valueOf:"(I)Ljava/lang/Integer;";iconst_2;invokestatic    Method java/lang/Integer.valueOf:"(I)Ljava/lang/Integer;";invokedynamic   InvokeDynamic  REF_invokeStatic:    Example.mybsm:      "(Ljava/lang/invoke/MethodHandles/Lookup;        Ljava/lang/String;        Ljava/lang/invoke/MethodType;)      Ljava/lang/invoke/CallSite;":    +:      "(Ljava/lang/Integer;        Ljava/lang/Integer;)      Ljava/lang/Integer;";

前四個指令將整數40和2放入堆棧並將它們裝入java.lang.Integer包裝器類型。第 5 條指令調用動態方法。該指令引用帶有CONSTANT_InvokeDynamic標記的常量池條目:The first four instructions put the integers 40 and 2 in the stack and boxes them in the java.lang.Integer wrapper type. The fifth instruction invokes a dynamic method. This instruction refers to a constant pool entry with a CONSTANT_InvokeDynamic tag:

REF_invokeStatic:  Example.mybsm:    "(Ljava/lang/invoke/MethodHandles/Lookup;      Ljava/lang/String;      Ljava/lang/invoke/MethodType;)    Ljava/lang/invoke/CallSite;":  +:    "(Ljava/lang/Integer;      Ljava/lang/Integer;)    Ljava/lang/Integer;";

CONSTANT_InvokeDynamic此條目中的標記後面有四個字節。Four bytes follow the CONSTANT_InvokeDynamic tag in this entry.

  • 前兩個字節形成對引用CONSTANT_MethodHandle引導程序方法說明符的條目的引用:The first two bytes form a reference to a CONSTANT_MethodHandleentry that references a bootstrap method specifier:

·         REF_invokeStatic:·           Example.mybsm:·             "(Ljava/lang/invoke/MethodHandles/Lookup;·               Ljava/lang/String;·               Ljava/lang/invoke/MethodType;)·             Ljava/lang/invoke/CallSite;"

這個對引導方法說明符的引用不在常量池表中。This reference to a bootstrap method specifier is not in the constant pool table.它包含在一個單獨的數組中,該數組由名爲 的類文件屬性定義BootstrapMethods。It is contained in a separate array defined by a class file attribute named BootstrapMethods. bootstrap 方法說明符包含一個CONSTANT_MethodHandle常量池條目的索引,它是 bootstrap 方法本身。The bootstrap method specifier contains an index to a CONSTANT_MethodHandle constant pool entry, which is the bootstrap method itself.

這個CONSTANT_MethodHandle常量池條目後面跟着三個字節:Three bytes follow this CONSTANT_MethodHandle constant pool entry:

  • 第一個字節是REF_invokeStatic子標籤。這意味着這個引導方法將爲靜態方法創建一個方法句柄;請注意,此引導程序方法將動態調用站點與靜態 Javaadder方法鏈接起來。The first byte is the REF_invokeStatic This means that this bootstrap method will create a method handle for a static method; note that this bootstrap method is linking the dynamic call site with the static Java addermethod.
  • 接下來的兩個字節形成一個CONSTANT_Methodref條目,表示要爲其創建方法句柄的方法:The next two bytes form a CONSTANT_Methodrefentry that represents the method for which the method handle is to be created:

·         Example.mybsm:·           "(Ljava/lang/invoke/MethodHandles/Lookup;·             Ljava/lang/String;·             Ljava/lang/invoke/MethodType;)·           Ljava/lang/invoke/CallSite;"

在此示例中,引導程序方法的完全限定名稱是Example.mybsm。該參數類型MethodHandles.Lookup,String以及MethodType。返回類型是CallSite。

In this example, the fully qualified name of the bootstrap method is Example.mybsm . The argument types are MethodHandles.Lookup, String, and MethodType. The return type is CallSite.

  • 接下來的兩個字節形成對CONSTANT_NameAndType條目的引用:The next two bytes form a reference to a CONSTANT_NameAndTypeentry:

·         +:·           "(Ljava/lang/Integer;·             Ljava/lang/Integer;)·           Ljava/lang/Integer;"

此常量池條目指定動態調用站點的方法名稱 ( +)、參數類型(兩個Integer實例)和返回類型 ( Integer)。This constant pool entry specifies the method name (+), the argument types (two Integer instances), and return type of the dynamic call site (Integer).

在此示例中,動態調用站點顯示爲裝箱整數值,這些值與最終目標的類型(adder方法)完全匹配。實際上,參數和返回類型不需要完全匹配。In this example, the dynamic call site is presented with boxed integer values, which exactly match the type of the eventual target, the adder method. In practice, the argument and return types don’t need to exactly match. 例如,invokedynamic指令可以將其一個或兩個操作數作爲原始int值傳遞到 JVM 堆棧上。一個或兩個操作數都可以是無類型Object值。該invokedynamic指令可以將其結果作爲原始int值或無類型Object值接收。 For example, the invokedynamic instruction could pass either or both of its operands on the JVM stack as primitive int values. Either or both operands could be untyped Object values. The invokedynamic instruction could receive its result as a primitive int value, or an untyped Object value. 無論如何,dynMethodType參數 tomybsm準確地描述了invokedynamic指令所需的方法類型。 In any case, the dynMethodType argument to mybsm accurately describes the method type that is required by the invokedynamic instruction.

該adder方法可以被賦予原始或無類型參數或返回值。The adder method could be given primitive or untyped arguments or return values.  bootstrap 方法負責彌補方法的dynMethodType和 類型之間的任何差異adder。如代碼所示,這可以通過asType調用目標方法輕鬆完成。The bootstrap method is responsible for making up any difference between the dynMethodType and the type of the adder method. As shown in the code, this is easily done with an asType call on the target method.

信號鏈

信號鏈使您能夠編寫需要安裝自己的信號處理程序的應用程序。此工具可在 Linux 和 macOS 上使用。

信號鏈工具具有以下特點:The signal chaining facility has the following features:

  • 創建 Oracle 的 HotSpot 虛擬機時支持預安裝的信號處理程序。Support for preinstalled signal handlers when you create Oracle’s HotSpot Virtual Machine.

創建 HotSpot VM 時,會保存 HotSpot VM 使用的信號的信號處理程序。在執行期間,當這些信號中的任何一個被引發並且不以 HotSpot VM 爲目標時,將調用預安裝的處理程序。換句話說,預裝的信號處理程序鏈接在這些信號的 HotSpot VM 處理程序後面。 During execution, when any of these signals are raised and are not to be targeted at the HotSpot VM, the preinstalled handlers are invoked. In other words, preinstalled signal handlers are chained behind the HotSpot VM handlers for these signals.

  • 支持在您創建 HotSpot VM 之後安裝的信號處理程序,無論是在 Java 本機接口代碼內還是來自另一個本機線程。Support for the signal handlers that are installed after you create the HotSpot VM, either inside the Java Native Interface code or from another native thread.

您的應用程序可以在libjsig.so庫之前鏈接和加載共享libc/libthread/libpthread庫。這個庫可以確保調用,例如signal(),sigset()和sigaction()如果處理程序與 HotSpot VM 已安裝的信號處理程序發生衝突,則不會替換 HotSpot VM 使用的信號處理程序。Your application can link and load the libjsig.so shared library before the libc/libthread/libpthread library. This library ensures that calls such as signal(), sigset(), and sigaction() are intercepted and don’t replace the signal handlers that are used by the HotSpot VM, if the handlers conflict with the signal handlers that are already installed by HotSpot VM. 相反,這些調用保存了新的信號處理程序。新的信號處理程序鏈接在信號的 HotSpot VM 信號處理程序後面。在執行期間,當這些信號中的任何一個被引發並且不是針對 HotSpot VM 時,預裝的處理程序就會被調用。Instead, these calls save the new signal handlers. The new signal handlers are chained behind the HotSpot VM signal handlers for the signals. During execution, when any of these signals are raised and are not targeted at the HotSpot VM, the preinstalled handlers are invoked.

筆記:

從 Java 16 開始,不推薦使用signal 和sigset函數,並且將在未來版本中刪除對這些函數的支持。改用該 sigaction函數。As of Java 16 the use of the signal and sigset functions are deprecated, and support for those functions will be removed in a future release. Use the sigaction function instead.

如果不需要在創建 VM 後支持信號處理程序安裝,則不需要libjsig.so共享庫。

要啓用信號鏈,請執行以下過程之一以使用libjsig.so共享庫:

  • 將so共享庫與創建或嵌入 HotSpot VM 的應用程序鏈接:

·         cc -L libjvm.so-directory -ljsig -ljvm java_application.c

  • 使用LD_PRELOAD環境變量:
    • 科恩殼 (ksh):Korn shell (ksh):

export LD_PRELOAD=libjvm.so-directory/libjsig.so; java_application

  • C外殼(csh):C shell (csh):

setenv LD_PRELOAD libjvm.so-directory/libjsig.so; java_application

插入的signal()、sigset()和sigaction()調用返回保存的信號處理程序,而不是由 HotSpot VM 安裝並被操作系統看到的信號處理程序。The interposed signal() , sigset() , and sigaction() calls return the saved signal handlers, not the signal handlers installed by the HotSpot VM and are seen by the operating system.

筆記:

在SIGQUIT,SIGTERM,SIGINT,和SIGHUP信號無法鏈接。如果應用程序必須處理這些信號,請考慮使用該—Xrs選項。The SIGQUIT, SIGTERM, SIGINT, and SIGHUP signals cannot be chained. If the application must handle these signals, then consider using the —Xrs option.

在 macOS 中啓用信號鏈

要在 macOS 中啓用信號鏈,請設置以下環境變量:

  • DYLD_INSERT_LIBRARIES:預加載指定的庫而不是LD_PRELOADLinux 上可用的環境變量。Preloads the specified libraries instead of the LD_PRELOADenvironment variable available on Linux.
  • DYLD_FORCE_FLAT_NAMESPACE:啓用libjsig庫中的函數並替換操作系統實現,因爲 macOS 的兩級命名空間(符號的完全限定名稱包括其庫)。要啓用此功能,請將此環境變量設置爲任何值。Enables functions in the libjsiglibrary and replaces the OS implementations, because of macOS’s two-level namespace (a symbol's fully qualified name includes its library). To enable this feature, set this environment variable to any value.

以下命令通過預加載libjsig庫啓用信號鏈:

$ DYLD_FORCE_FLAT_NAMESPACE=0 DYLD_INSERT_LIBRARIES="JAVA_HOME/lib/libjsig.dylib" java MySpiffyJavaApp

筆記:

在Mac OS的庫文件名是 libjsig.dylib不是libjsig.so因爲它是在Linux上。

本機內存跟蹤

本章介紹本機內存跟蹤 (NMT) 功能 Native Memory Tracking (NMT) feature. 。NMT 是一項 Java Hotspot VM 功能,用於跟蹤 HotSpot VM 的內部內存使用情況。NMT is a Java Hotspot VM feature that tracks internal memory usage for a HotSpot VM.您可以使用該jcmd實用程序訪問 NMT 數據。 You can access NMT data by using the jcmd utility. NMT 不跟蹤第三方本機代碼和 Oracle Java 開發工具包 (JDK) 類庫的內存分配。NMTMBean在 HotSpot for Java Mission Control (JMC)中不包括 NMT 。NMT does not track memory allocations for third-party native code and Oracle Java Development Kit (JDK) class libraries. NMT does not include NMT MBean in HotSpot for Java Mission Control (JMC).

話題:

主要特點Key Features

當您將本機內存跟蹤與 結合使用時jcmd,您可以在不同級別跟蹤 Java 虛擬機 (JVM) 或 HotSpot VM 內存使用情況。NMT 僅跟蹤 JVM 或 HotSpot VM 使用的內存,而不是用戶的本機內存。NMT 沒有提供類數據共享 (CDS) 存檔使用的內存的完整信息。When you use Native Memory Tracking with jcmd, you can track Java Virtual Machine (JVM) or HotSpot VM memory usage at different levels. NMT tracks only the memory that the JVM or HotSpot VM uses, not the user's native memory. NMT doesn't give complete information for the memory used by the class data sharing (CDS) archive.

默認情況下,HotSpot VM 的 NMT 處於關閉狀態。您可以使用 JVM 命令行選項打開 NMT。見的Java在Java開發工具包工具的規格有關高級運行選項的信息。NMT for HotSpot VM is turned off by default. You can turn on NMT by using the JVM command-line option. See java in the Java Development Kit Tool Specifications for information about advanced runtime options.

您可以使用該jcmd實用程序訪問 NMT 。請參閱使用 jcmd 訪問 NMT 數據。您可以使用該jcmd實用程序停止 NMT ,但您無法使用該實用程序啓動或重新啓動 NMT jcmd。You can access NMT using the jcmd utility. See Use jcmd to Access NMT Data. You can stop NMT by using the jcmd utility, but you can't start or restart NMT by using the jcmd utilty.

NMT 支持以下功能:

  • 生成摘要和詳細報告。Generate summary and detail reports.
  • 爲以後的比較建立一個早期的基線。Establish an early baseline for later comparison.
  • 使用 JVM 命令行選項在 JVM 退出時請求內存使用情況報告。請參閱VM 出口處的 NMT。Request a memory usage report at JVM exit with the JVM command-line option. See NMT at VM exit.

使用本機內存跟蹤Using Native Memory Tracking

您必須啓用 NMT,然後使用該jcmd實用程序訪問 NMT 數據。You must enable NMT and then use the jcmd utility to access the NMT data.

啓用 NMT

要啓用 NMT,請使用以下命令行選項:

-XX:NativeMemoryTracking=[off | summary | detail]

筆記:啓用 NMT 會導致 5% -10% 的性能開銷。Enabling NMT causes a 5% -10% performance overhead.

下表描述了 NMT 命令行使用選項:

表 9-1 NMT 使用選項

NMT 選項

描述

off

NMT off 默認開啓。NMT is turned off by default.

summary

僅收集子系統彙總的內存使用情況。Collect only memory usage aggregated by subsystem.

detail

收集各個調用站點的內存使用情況。Collect the memory usage by individual call sites.

使用 jcmd 訪問 NMT 數據

使用jcmd轉儲所收集的數據以及可選的數據進行比較,以最後的底線。

jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]

表 9-2 jcmd NMT 選項

jcmd NMT 選項

描述

summary

打印按類別彙總的摘要。

detail

  • 打印內存使用情況,按類別彙總
  • 打印虛擬內存映射
  • 打印內存使用情況,按調用站點彙總

baseline

創建一個新的內存使用情況快照進行比較。

summary.diff

針對最後一個基線打印一份新的總結報告。

detail.diff

針對最後一個基線打印新的詳細報告。

shutdown

停止 NMT。

VM出口獲​​取NMT數據Obtaining NMT Data at VM Exit

要在 VM 退出時獲取上次內存使用情況的數據,請在啓用本機內存跟蹤時使用以下 VM 診斷命令行選項。詳細程度基於跟蹤級別。To obtain data for the last memory usage at VM exit, when Native Memory Tracking is enabled, use the following VM diagnostic command-line options. The level of detail is based on tracking level.

-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics

有關如何監視 VM 內部內存分配和診斷 VM 內存泄漏的信息,請參閱Java Platform, Standard Edition Troubleshooting Guide中的Native Memory Tracking

HotSpot VM 中的DTrace 探針

本章介紹 Oracle 的 HotSpot VM 中的 DTrace 支持。這hotspot 和 hotspot_jni提供程序允許您訪問探針,您可以使用這些探針來監視與 Java 虛擬機 (JVM) 的內部狀態和活動一起運行的 Java 應用程序。所有的探測器都是USDT探測器,你可以通過JVM進程的process-id來訪問它們。

This chapter describes DTrace support in Oracle’s HotSpot VM. The hotspot and hotspot_jni providers let you access probes that you can use to monitor the Java application that is running together with the internal state and activities of the Java Virtual Machine (JVM). All of the probes are USDT probes and you can access them by using the process-id of the JVM process.

話題:

使用熱點提供程序Using the hotspot Provider

這 hotspotprovider 允許您訪問可用於跟蹤 VM 的生命週期、線程啓動和停止事件、垃圾收集器 (GC) 和內存池統計信息、方法編譯和監視活動的探測器。The hotspot provider lets you access probes that you can use to track the lifespan of the VM, thread start and stop events, garbage collector (GC) and memory pool statistics, method compilations, and monitor activity. 啓動標誌可以啓用額外的探測,您可以使用這些探測來監視正在運行的 Java 程序,例如對象分配和方法進入和返回探測。這hotspot 探針源自 VM 庫 (libjvm.so),因此它們由嵌入 VM 的程序提供。 A startup flag can enable additional probes that you can use to monitor the running Java program, such as object allocations and method enter and return probes. The hotspot probes originate in the VM library (libjvm.so), so they are provided from programs that embed the VM.

提供程序中的許多探測器都有提供有關 VM 狀態的更多詳細信息的參數。許多這些參數是不透明的 ID,可用於將探測器觸發相互鏈接。Many of the probes in the provider have arguments for providing further details on the state of the VM. Many of these arguments are opaque IDs which can be used to link probe firings to each other. 但是,還提供了字符串和其他數據。當提供字符串值時,它們總是成對出現:一個指向未終止的已修改 UTF-8 數據的指針(請參閱JVM 規範),以及一個指示該數據範圍的長度值。字符串數據不保證以NUL字符結尾,需要使用長度終止的copyinstr()intrinsic來讀取字符串數據。即使沒有任何字符超出 ASCII 範圍,也是如此。However, strings and other data are also provided. When string values are provided, they are always present as a pair: a pointer to unterminated modified UTF-8 data (see the JVM Specification) , and a length value which indicates the extent of that data. The string data is not guaranteed to be terminated by a NUL character, and it is necessary to use the length-terminated copyinstr() intrinsic to read the string data. This is true even when none of the characters are outside the ASCII range.

虛擬機生命週期探測

以下探針可用於跟蹤 VM 生命週期活動。沒有任何論據。The following probes are available for tracking VM lifecycle activities. None have any arguments.

表 10-1 VM 生命週期探測

探測

描述

vm-init-begin

VM初始化開始時啓動的探測器Probe that starts when the VM initialization begins

vm-init-end

當 VM 初始化完成並且 VM 準備好開始運行應用程序代碼時啓動的探測器Probe that starts when the VM initialization finishes, and the VM is ready to start running application code

vm-shutdown

由於程序終止或錯誤而關閉 VM 時啓動的探測器Probe that starts as the VM is shuts down due to program termination or an error

線程生命週期探針

以下探測器可用於跟蹤線程啓動和停止事件。

探測

描述

thread-start

線程啓動時啓動的探測器。

thread-stop

線程完成時啓動的探測器。

以下參數可用於線程生命週期探測:The following argument are available for the thread lifecycle probes:

探測參數

描述

args[0]

指向包含線程名稱的 UTF-8 字符串數據的指針。A pointer to UTF-8 string data that contains the thread name.

args[1]

線程名稱數據的長度(以字節爲單位)。The length of the thread name data (in bytes).

args[2]

Java 線程 ID。此值與包含線程參數的其他 HotSpot VM 探測器匹配。The length of the thread name data (in bytes).

args[3]

本機或操作系統線程 ID。此 ID 由主機操作系統分配。The native or OS thread ID. This ID is assigned by the host operating system.

args[4]

一個布爾值,指示此線程是否爲守護進程。值 0 表示非守護線程。A boolean value that indicates whether this thread is a daemon or not. A value of 0 indicates a non-daemon thread.

類加載探針Classloading Probes

以下探測器可用於跟蹤類加載和卸載活動。The following probes are available for tracking class loading and unloading activity.

探測

描述

class-loaded

加載類時觸發的探測器Probe that fires when a class is loaded

class-unloaded

從系統卸載類時觸發的探測器Probe that fires when a class is unloaded from the system

以下參數可用於classloading探測器:

探測參數

描述

args[0]

指向 UTF-8 字符串數據的指針,其中包含加載的類的名稱A pointer to UTF-8 string data that contains the name of the class that is loaded

args[1]

類名數據的長度(以字節爲單位)The length of the class name data (in bytes)

args[2]

類加載器 ID,它是 VM 中類加載器的唯一標識符。(這是加載類的類加載器。)The class loader ID, which is a unique identifier for a class loader in the VM. (This is the class loader that loaded the class.)

args[3]

一個布爾值,指示該類是否爲共享類(如果該類是從共享存檔加載的)A boolean value that indicates whether the class is a shared class (if the class was loaded from the shared archive)

垃圾收集探針Garbage Collection Probes

可以使用探針來測量系統範圍的垃圾收集週期的持續時間(對於那些具有定義開始和結束的垃圾收集器)。每個內存池都被獨立跟蹤。單個池的探測器在池收集的開始和結束時傳遞內存管理器的名稱、池名稱和池使用信息。Probes are available that you can use to measure the duration of a system-wide garbage collection cycle (for those garbage collectors that have a defined begin and end). Each memory pool is tracked independently. The probes for individual pools pass the memory manager's name, the pool name, and pool usage information at both the beginning and ending of pool collection.

以下探測器可用於垃圾收集活動:The following probes are available for garbage collecting activities:

探測

描述

gc-begin

當系統範圍的集合開始時啓動的探測器。此探測可用的一個參數 ( arg[0]) 是一個布爾值,指示是否執行 Full GC。Probe that starts when a system-wide collection starts. The one argument available for this probe, (arg[0]), is a boolean value that indicates whether to perform a Full GC.

gc-end

當系統範圍的收集完成時啓動的探測器。沒有爭論。Probe that starts when a system-wide collection is completed. No arguments.

mem-pool-gc-begin

收集單個內存池時啓動的探測器。Probe that starts when an individual memory pool is collected.

mem-pool-gc-end

在收集單個內存池後啓動的探測器。Probe that starts after an individual memory pool is collected.

以下參數可用於內存池探測:

探測參數

描述

args[0]

指向包含管理此內存池的管理器名稱的 UTF-8 字符串數據的指針。A pointer to the UTF-8 string data that contains the name of the manager that manages this memory pool.

args[1]

管理器名稱數據的長度(以字節爲單位)。The length of the manager name data (in bytes).

args[2]

指向包含內存池名稱的 UTF-8 字符串數據的指針。A pointer to the UTF-8 string data that contains the name of the memory pool.

args[3]

內存池名稱數據的長度(以字節爲單位)。The length of the memory pool name data (in bytes).

args[4]

內存池的初始大小(以字節爲單位)。The initial size of the memory pool (in bytes).

args[5]

內存池中使用的內存量(以字節爲單位)。The amount of memory in use in the memory pool (in bytes).

args[6]

內存池中提交的頁面數。The number of committed pages in the memory pool.

args[7]

內存池的最大大小。The maximum size of the memory pool.

方法編譯探針Method Compilation Probes

探測器可用於指示正​​在編譯哪些方法以及由哪個編譯器編譯,並跟蹤已編譯方法的安裝或卸載時間。Probes are available to indicate which methods are being compiled and by which compiler, and to track when the compiled methods are installed or uninstalled.

以下探針可用於標記方法編譯的開始和結束:

探測

描述

method-compile-begin

方法編譯開始時啓動的探測器。Probe that starts when the method compilation begins.

method-compile-end

方法編譯完成時啓動的探測器。除了以下參數外,該argv[8]參數是一個布爾值,表示編譯是否成功。Probe that starts when method compilation is completed. In addition to the following arguments, the argv[8] argument is a boolean value that indicates whether the compilation was successful.

以下參數可用於方法編譯探測:

探測參數

描述

args[0]

指向包含編譯此方法的編譯器名稱的 UTF-8 字符串數據的指針。A pointer to UTF-8 string data that contains the name of the compiler that is compiling this method.

args[1]

編譯器名稱數據的長度(以字節爲單位)。The length of the compiler name data (in bytes).

args[2]

指向 UTF-8 字符串數據的指針,其中包含正在編譯的方法的類的名稱。A pointer to UTF-8 string data that contains the name of the class of the method being compiled.

args[3]

類名數據的長度(以字節爲單位)。The length of the class name data (in bytes).

args[4]

指向 UTF-8 字符串數據的指針,其中包含正在編譯的方法的名稱。A pointer to UTF-8 string data that contains the name of the method being compiled.

args[5]

方法名稱數據的長度(以字節爲單位)。The length of the method name data (in bytes).

args[6]

指向 UTF-8 字符串數據的指針,其中包含正在編譯的方法的簽名。A pointer to UTF-8 string data that contains the signature of the method being compiled.

args[7]

簽名數據的長度(以字節爲單位)。The length of the signature data (in bytes).

安裝或卸載編譯的方法時,以下探測器可用:The following probes are available when compiled methods are installed for execution or uninstalled:

探測

描述

compiled-method-load

安裝編譯方法時啓動的探測器。附加參數argv[6]包含一個指向編譯代碼的指針,是編譯代碼argv[7]的大小。Probe that starts when a compiled method is installed. The additional argument, argv[6] contains a pointer to the compiled code, and the argv[7] is the size of the compiled code.

compiled-method-unload

卸載編譯的方法時啓動的探測器。Probe that starts when a compiled method is uninstalled.

以下參數可用於已編譯的方法加載探針:

探測參數

描述

args[0]

指向 UTF-8 字符串數據的指針,其中包含正在安裝的方法的類的名稱。A pointer to UTF-8 string data that contains the name of the class of the method being installed.

args[1]

類名數據的長度(以字節爲單位)。The length of the class name data (in bytes).

args[2]

指向 UTF-8 字符串數據的指針,其中包含正在安裝的方法的名稱。A pointer to UTF-8 string data that contains the name of the method being installed.

args[3]

方法名稱數據的長度(以字節爲單位)。The length of the method name data (in bytes).

args[4]

指向 UTF-8 字符串數據的指針,其中包含正在安裝的方法的簽名。A pointer to UTF-8 string data that contains the signature of the method being installed.

args[5]

簽名數據的長度(以字節爲單位)。The length of the signature data (in bytes).

監控探頭

當您的 Java 應用程序運行時,線程進入和退出監視器、等待監視器並執行通知。探測可用於所有等待和通知事件,以及競爭監視器進入和退出事件。When your Java application runs, threads enter and exit monitors, wait on monitors, and perform notifications. Probes are available for all wait and notification events, and for contended monitor entry and exit events.

當一個線程試圖進入監視器而另一個線程在監視器中時,就會發生競爭監視器條目。當一個線程離開監視器而其他線程正在等待進入監視器時,就會發生競爭監視器退出事件。與遇到這些事件的線程相關的競爭監視器條目和競爭監視器退出事件可能彼此不匹配,儘管預計一個線程的競爭退出將匹配另一個線程(等待進入該線程的線程)的競爭進入。監視器)。A contended monitor entry occurs when a thread attempts to enter a monitor while another thread is in the monitor. A contended monitor exit event occurs when a thread leaves a monitor while other threads are waiting to enter to the monitor. The contended monitor entry and contended monitor exit events might not match each other in relation to the thread that encounters these events, athough a contended exit from one thread is expected to match up to a contended enter on another thread (the thread waiting to enter the monitor).

監視器事件提供線程 ID、監視器 ID 和對象類的類型作爲參數。線程 ID 和類類型可以映射回 Java 程序,而監視器 ID 可以提供探測觸發之間的匹配信息。Monitor events provide the thread ID, a monitor ID, and the type of the class of the object as arguments. The thread ID and the class type can map back to the Java program, while the monitor ID can provide matching information between probe firings.

VM 中存在這些探測器會降低性能,並且它們僅在-XX:+ExtendedDTraceProbesJava 命令行上設置標誌時啓動。使用該jinfo實用程序在運行時動態打開和關閉此標誌。The existence of these probes in the VM degrades performance and they start only when the -XX:+ExtendedDTraceProbes flag is set on the Java command line. This flag is turned on and off dynamically at runtime by using the jinfo utility.

如果該標誌關閉,則可從 Dtrace 獲得的探針列表中存在監控探針,但探針仍處於休眠狀態且不會啓動。計劃在 VM 的未來版本中取消此限制,並且將啓用這些探測,而不會影響性能。

If the flag is off, the monitor probes are present in the probe listing that is obtainable from Dtrace, but the probes remain dormant and don’t start. Removal of this restriction is planned for future releases of the VM, and these probes will be enabled with no impact to performance.

以下探測器可用於監視事件:

探測

描述

monitor-contended-enter

當線程試圖進入競爭監視器時啓動的探測器Probe that starts when a thread attempts to enter a contended monitor

monitor-contended-entered

當線程成功進入競爭監視器時啓動的探測器Probe that starts when a thread successfully enters the contended monitor

monitor-contended-exit

當一個線程離開監視器而其他線程正在等待進入時啓動的探測器Probe that starts when a thread leaves a monitor and other threads are waiting to enter

monitor-wait

當線程開始在監視器上等待時啓動的探測器使用Object.wait(). 附加參數args[4]是一個長值,指示正在使用的超時。Probe that starts when a thread begins a wait on a monitor by using the Object.wait(). The additional argument, args[4] is a long value that indicates the timeout being used.

monitor-waited

當線程完成一個Object.wait()動作時開始的探測器。Probe that starts when a thread completes an Object.wait() action.

monitor-notify

當線程調用Object.notify()以通知監視器上的等待程序時啓動的探測器。Probe that starts when a thread calls Object.notify() to notify waiters on a monitor.

monitor-notifyAll

當線程調用Object.notifyAll()以通知監視器上的等待程序時啓動的探測器。Probe that starts when a thread calls Object.notifyAll() to notify waiters on a monitor.

以下參數可用於監視器:

探測參數

描述

args[0]

執行監視器操作的線程的 Java 線程標識符。The Java thread identifier for the thread performing the monitor operation.

args[1]

執行操作的特定監視器的唯一但不透明的標識符。A unique, but opaque identifier for the specific monitor that the action is performed upon.

args[2]

指向 UTF-8 字符串數據的指針,其中包含正在處理的對象的類名。A pointer to UTF-8 string data which contains the class name of the object being acted upon.

args[3]

類名數據的長度(以字節爲單位)。The length of the class name data (in bytes).

應用程序跟蹤探針Application Tracking Probes

您可以使用探針來對 Java 線程執行進行細粒度檢查。應用程序跟蹤探測在方法進入或返回時啓動,或者在 Java 對象已分配時啓動。You can use probes to allow fine-grained examination of Java thread execution. Application tracking probes start when a method is entered or returned from, or when a Java object has been allocated.

VM 中存在這些探測器會降低性能,並且它們僅在 VMExtendedDTraceProbes啓用標誌時啓動。默認情況下,探針出現在 VM 中的任何探針列表中,但處於休眠狀態,沒有適當的標誌。計劃在 VM 的未來版本中取消此限制,並且將啓用這些探測,不會對性能產生影響。The existence of these probes in the VM degrades performance and they start only when the VM has the ExtendedDTraceProbes flag enabled. By default, the probes are present in any listing of the probes in the VM, but are dormant without the appropriate flag. Removal of this restriction is planned in future releases of the VM, and these probes will be enabled no impact to performance.

以下探針可用於方法進入和退出:

探測

描述

method-entry

在輸入方法時啓動的探測器。Probe that starts when a method is being entered.

method-return

當方法返回時啓動的探測器,無論是正常的還是由於異常。Probe that starts when a method returns, either normally or due to an exception.

以下參數可用於方法入口和出口:

探測參數

描述

args[0]

進入或離開方法的線程的 Java 線程 ID。The Java thread ID of the thread that is entering or leaving the method.

args[1]

指向包含方法類名稱的 UTF-8 字符串數據的指針。A pointer to UTF-8 string data that contains the name of the class of the method.

args[2]

類名數據的長度(以字節爲單位)。The length of the class name data (in bytes).

args[3]

指向包含方法名稱的 UTF-8 字符串數據的指針。A pointer to UTF-8 string data that contains the name of the method.

args[4]

方法名稱數據的長度(以字節爲單位)。The length of the method name data (in bytes).

args[5]

指向包含方法簽名的 UTF-8 字符串數據的指針。A pointer to UTF-8 string data that contains the signature of the method.

args[6]

簽名數據的長度(以字節爲單位)。The length of the signature data (in bytes).

以下探針可用於對象分配:

探測

描述

object-alloc

如果ExtendedDTraceProbes啓用了該標誌,則在分配任何對象時啓動的探測器。Probe that starts when any object is allocated, provided that the ExtendedDTraceProbes flag is enabled.

以下參數可用於對象分配探測:

探測參數

描述

args[0]

分配對象的線程的 Java 線程 ID。The Java thread ID of the thread that is allocating the object.

args[1]

指向 UTF-8 字符串數據的指針,其中包含正在分配的對象的類名。A pointer to UTF-8 string data that contains the class name of the object being allocated.

args[2]

類名數據的長度(以字節爲單位)。The length of the class name data (in bytes).

args[3]

正在分配的對象的大小。The size of the object being allocated.

使用 hotspot_jni 提供程序Using the hotspot_jni Provider

爲了從本機代碼調用 Java 代碼,由於將 VM 嵌入應用程序或在 Java 應用程序中執行本機代碼,本機代碼必須通過 Java 本機接口 (JNI) 進行調用。JNI 提供了許多調用 Java 代碼和檢查 VM 狀態的方法。在每個方法的入口點和返回點都提供了 DTrace 探測器。探針由hotspot_jni提供者。探測器的名稱是 JNI 方法的名稱,附加-entry爲入口探測器和-return返回探測器。每個入口探針中可用的參數是提供給函數Invoke*的參數,方法除外,它們省略了傳遞給 Java 方法的參數。返回探測器將方法的返回值作爲參數(如果可用)。In order to call from native code to Java code, due to embedding of the VM in an application or execution of native code within a Java application, the native code must make a call through the Java Native Interface (JNI). The JNI provides a number of methods for invoking Java code and examining the state of the VM. DTrace probes are provided at the entry point and return point for each of these methods. The probes are provided by the hotspot_jni provider. The name of the probe is the name of the JNI method, appended with -entry for entry probes, and -return for return probes. The arguments available at each entry probe are the arguments that were provided to the function, with the exception of the Invoke* methods, which omit the arguments that are passed to the Java method. The return probes have the return value of the method as an argument (if available).

DTrace 探針示例Sample DTrace Probes()

 provider hotspot {  probe vm-init-begin();  probe vm-init-end();  probe vm-shutdown();  probe class-loaded(      char* class_name, uintptr_t class_name_len, uintptr_t class_loader_id, bool is_shared);  probe class-unloaded(      char* class_name, uintptr_t class_name_len, uintptr_t class_loader_id, bool is_shared);  probe gc-begin(bool is_full);  probe gc-end();  probe mem-pool-gc-begin(      char* mgr_name, uintptr_t mgr_name_len, char* pool_name, uintptr_t pool_name_len,       uintptr_t initial_size, uintptr_t used, uintptr_t committed, uintptr_t max_size);  probe mem-pool-gc-end(      char* mgr_name, uintptr_t mgr_name_len, char* pool_name, uintptr_t pool_name_len,       uintptr_t initial_size, uintptr_t used, uintptr_t committed, uintptr_t max_size);  probe thread-start(      char* thread_name, uintptr_t thread_name_length,       uintptr_t java_thread_id, uintptr_t native_thread_id, bool is_daemon);  probe thread-stop(      char* thread_name, uintptr_t thread_name_length,       uintptr_t java_thread_id, uintptr_t native_thread_id, bool is_daemon);  probe method-compile-begin(      char* class_name, uintptr_t class_name_len,       char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len);  probe method-compile-end(      char* class_name, uintptr_t class_name_len,       char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len,      bool is_success);  probe compiled-method-load(      char* class_name, uintptr_t class_name_len,       char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len,      void* code, uintptr_t code_size);  probe compiled-method-unload(      char* class_name, uintptr_t class_name_len,       char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len);  probe monitor-contended-enter(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe monitor-contended-entered(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe monitor-contended-exit(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe monitor-wait(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len,      uintptr_t timeout);  probe monitor-waited(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe monitor-notify(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe monitor-notifyAll(      uintptr_t java_thread_id, uintptr_t monitor_id,       char* class_name, uintptr_t class_name_len);  probe method-entry(      uintptr_t java_thread_id, char* class_name, uintptr_t class_name_len,      char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len);  probe method-return(      uintptr_t java_thread_id, char* class_name, uintptr_t class_name_len,      char* method_name, uintptr_t method_name_len,      char* signature, uintptr_t signature_len);  probe object-alloc(      uintptr_t java_thread_id, char* class_name, uintptr_t class_name_len,      uintptr_t size);}; provider hotspot_jni {  probe AllocObject-entry(void*, void*);  probe AllocObject-return(void*);  probe AttachCurrentThreadAsDaemon-entry(void*, void**, void*);  probe AttachCurrentThreadAsDaemon-return(uint32_t);  probe AttachCurrentThread-entry(void*, void**, void*);  probe AttachCurrentThread-return(uint32_t);  probe CallBooleanMethodA-entry(void*, void*, uintptr_t);  probe CallBooleanMethodA-return(uintptr_t);  probe CallBooleanMethod-entry(void*, void*, uintptr_t);  probe CallBooleanMethod-return(uintptr_t);  probe CallBooleanMethodV-entry(void*, void*, uintptr_t);  probe CallBooleanMethodV-return(uintptr_t);  probe CallByteMethodA-entry(void*, void*, uintptr_t);  probe CallByteMethodA-return(char);  probe CallByteMethod-entry(void*, void*, uintptr_t);  probe CallByteMethod-return(char);  probe CallByteMethodV-entry(void*, void*, uintptr_t);   probe CallByteMethodV-return(char);  probe CallCharMethodA-entry(void*, void*, uintptr_t);  probe CallCharMethodA-return(uint16_t);  probe CallCharMethod-entry(void*, void*, uintptr_t);  probe CallCharMethod-return(uint16_t);  probe CallCharMethodV-entry(void*, void*, uintptr_t);  probe CallCharMethodV-return(uint16_t);  probe CallDoubleMethodA-entry(void*, void*, uintptr_t);  probe CallDoubleMethodA-return(double);  probe CallDoubleMethod-entry(void*, void*, uintptr_t);  probe CallDoubleMethod-return(double);  probe CallDoubleMethodV-entry(void*, void*, uintptr_t);  probe CallDoubleMethodV-return(double);  probe CallFloatMethodA-entry(void*, void*, uintptr_t);  probe CallFloatMethodA-return(float);  probe CallFloatMethod-entry(void*, void*, uintptr_t);  probe CallFloatMethod-return(float);  probe CallFloatMethodV-entry(void*, void*, uintptr_t);  probe CallFloatMethodV-return(float);  probe CallIntMethodA-entry(void*, void*, uintptr_t);  probe CallIntMethodA-return(uint32_t);  probe CallIntMethod-entry(void*, void*, uintptr_t);  probe CallIntMethod-return(uint32_t);  probe CallIntMethodV-entry(void*, void*, uintptr_t);  probe CallIntMethodV-return(uint32_t);  probe CallLongMethodA-entry(void*, void*, uintptr_t);  probe CallLongMethodA-return(uintptr_t);  probe CallLongMethod-entry(void*, void*, uintptr_t);  probe CallLongMethod-return(uintptr_t);  probe CallLongMethodV-entry(void*, void*, uintptr_t);  probe CallLongMethodV-return(uintptr_t);  probe CallNonvirtualBooleanMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualBooleanMethodA-return(uintptr_t);  probe CallNonvirtualBooleanMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualBooleanMethod-return(uintptr_t);  probe CallNonvirtualBooleanMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualBooleanMethodV-return(uintptr_t);  probe CallNonvirtualByteMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualByteMethodA-return(char);  probe CallNonvirtualByteMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualByteMethod-return(char);  probe CallNonvirtualByteMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualByteMethodV-return(char);  probe CallNonvirtualCharMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualCharMethodA-return(uint16_t);  probe CallNonvirtualCharMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualCharMethod-return(uint16_t);  probe CallNonvirtualCharMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualCharMethodV-return(uint16_t);  probe CallNonvirtualDoubleMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualDoubleMethodA-return(double);  probe CallNonvirtualDoubleMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualDoubleMethod-return(double);  probe CallNonvirtualDoubleMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualDoubleMethodV-return(double);  probe CallNonvirtualFloatMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualFloatMethodA-return(float);  probe CallNonvirtualFloatMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualFloatMethod-return(float);  probe CallNonvirtualFloatMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualFloatMethodV-return(float);  probe CallNonvirtualIntMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualIntMethodA-return(uint32_t);  probe CallNonvirtualIntMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualIntMethod-return(uint3t);  probe CallNonvirtualIntMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualIntMethodV-return(uint32_t);  probe CallNonvirtualLongMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualLongMethodA-return(uintptr_t);  probe CallNonvirtualLongMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualLongMethod-return(uintptr_t);  probe CallNonvirtualLongMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualLongMethodV-return(uintptr_t);  probe CallNonvirtualObjectMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualObjectMethodA-return(void*);  probe CallNonvirtualObjectMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualObjectMethod-return(void*);  probe CallNonvirtualObjectMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualObjectMethodV-return(void*);  probe CallNonvirtualShortMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualShortMethodA-return(uint16_t);  probe CallNonvirtualShortMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualShortMethod-return(uint16_t);  probe CallNonvirtualShortMethodV-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualShortMethodV-return(uint16_t);  probe CallNonvirtualVoidMethodA-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualVoidMethodA-return();  probe CallNonvirtualVoidMethod-entry(void*, void*, void*, uintptr_t);  probe CallNonvirtualVoidMethod-return();  probe CallNonvirtualVoidMethodV-entry(void*, void*, void*, uintptr_t);    probe CallNonvirtualVoidMethodV-return();  probe CallObjectMethodA-entry(void*, void*, uintptr_t);  probe CallObjectMethodA-return(void*);  probe CallObjectMethod-entry(void*, void*, uintptr_t);  probe CallObjectMethod-return(void*);  probe CallObjectMethodV-entry(void*, void*, uintptr_t);  probe CallObjectMethodV-return(void*);  probe CallShortMethodA-entry(void*, void*, uintptr_t);  probe CallShortMethodA-return(uint16_t);  probe CallShortMethod-entry(void*, void*, uintptr_t);  probe CallShortMethod-return(uint16_t);  probe CallShortMethodV-entry(void*, void*, uintptr_t);  probe CallShortMethodV-return(uint16_t);  probe CallStaticBooleanMethodA-entry(void*, void*, uintptr_t);  probe CallStaticBooleanMethodA-return(uintptr_t);  probe CallStaticBooleanMethod-entry(void*, void*, uintptr_t);  probe CallStaticBooleanMethod-return(uintptr_t);  probe CallStaticBooleanMethodV-entry(void*, void*, uintptr_t);  probe CallStaticBooleanMethodV-return(uintptr_t);  probe CallStaticByteMethodA-entry(void*, void*, uintptr_t);  probe CallStaticByteMethodA-return(char);  probe CallStaticByteMethod-entry(void*, void*, uintptr_t);  probe CallStaticByteMethod-return(char);  probe CallStaticByteMethodV-entry(void*, void*, uintptr_t);  probe CallStaticByteMethodV-return(char);  probe CallStaticCharMethodA-entry(void*, void*, uintptr_t);  probe CallStaticCharMethodA-return(uint16_t);  probe CallStaticCharMethod-entry(void*, void*, uintptr_t);  probe CallStaticCharMethod-return(uint16_t);  probe CallStaticCharMethodV-entry(void*, void*, uintptr_t);  probe CallStaticCharMethodV-return(uint16_t);  probe CallStaticDoubleMethodA-entry(void*, void*, uintptr_t);  probe CallStaticDoubleMethodA-return(double);  probe CallStaticDoubleMethod-entry(void*, void*, uintptr_t);  probe CallStaticDoubleMethod-return(double);  probe CallStaticDoubleMethodV-entry(void*, void*, uintptr_t);  probe CallStaticDoubleMethodV-return(double);  probe CallStaticFloatMethodA-entry(void*, void*, uintptr_t);  probe CallStaticFloatMethodA-return(float);  probe CallStaticFloatMethod-entry(void*, void*, uintptr_t);  probe CallStaticFloatMethod-return(float);  probe CallStaticFloatMethodV-entry(void*, void*, uintptr_t);  probe CallStaticFloatMethodV-return(float);  probe CallStaticIntMethodA-entry(void*, void*, uintptr_t);  probe CallStaticIntMethodA-return(uint32_t);  probe CallStaticIntMethod-entry(void*, void*, uintptr_t);  probe CallStaticIntMethod-return(uint32_t);  probe CallStaticIntMethodentry(void*, void*, uintptr_t);  probe CallStaticIntMethodV-return(uint32_t);  probe CallStaticLongMethodA-entry(void*, void*, uintptr_t);  probe CallStaticLongMethodA-return(uintptr_t);  probe CallStaticLongMethod-entry(void*, void*, uintptr_t);  probe CallStaticLongMethod-return(uintptr_t);  probe CallStaticLongMethodV-entry(void*, void*, uintptr_t);  probe CallStaticLongMethodV-return(uintptr_t);  probe CallStaticObjectMethodA-entry(void*, void*, uintptr_t);  probe CallStaticObjectMethodA-return(void*);  probe CallStaticObjectMethod-entry(void*, void*, uintptr_t);  probe CallStaticObjectMethod-return(void*);  probe CallStaticObjectMethodV-entry(void*, void*, uintptr_t);  probe CallStaticObjectMethodV-return(void*);  probe CallStaticShortMethodA-entry(void*, void*, uintptr_t);  probe CallStaticShortMethodA-return(uint16_t);  probe CallStaticShortMethod-entry(void*, void*, uintptr_t);  probe CallStaticShortMethod-return(uint16_t);  probe CallStaticShortMethodV-entry(void*, void*, uintptr_t);  probe CallStaticShortMethodV-return(uint16_t);  probe CallStaticVoidMethodA-entry(void*, void*, uintptr_t);  probe CallStaticVoidMethodA-return();  probe CallStaticVoidMethod-entry(void*, void*, uintptr_t);  probe CallStaticVoidMethod-return();   probe CallStaticVoidMethodV-entry(void*, void*, uintptr_t);    probe CallStaticVoidMethodV-return();  probe CallVoidMethodA-entry(void*, void*, uintptr_t);    probe CallVoidMethodA-return();  probe CallVoidMethod-entry(void*, void*, uintptr_t);    probe CallVoidMethod-return();   probe CallVoidMethodV-entry(void*, void*, uintptr_t);    probe CallVoidMethodV-return();  probe CreateJavaVM-entry(void**, void**, void*);  probe CreateJavaVM-return(uint32_t);  probe DefineClass-entry(void*, const char*, void*, char, uintptr_t);  probe DefineClass-return(void*);  probe DeleteGlobalRef-entry(void*, void*);  probe DeleteGlobalRef-return();  probe DeleteLocalRef-entry(void*, void*);  probe DeleteLocalRef-return();  probe DeleteWeakGlobalRef-entry(void*, void*);  probe DeleteWeakGlobalRef-return();  probe DestroyJavaVM-entry(void*);  probe DestroyJavaVM-return(uint32_t);  probe DetachCurrentThread-entry(void*);  probe DetachCurrentThread-return(uint32_t);  probe EnsureLocalCapacity-entry(void*, uint32_t);  probe EnsureLocalCapacity-return(uint32_t);  probe ExceptionCheck-entry(void*);  probe ExceptionCheck-return(uintptr_t);  probe ExceptionClear-entry(void*);  probe ExceptionClear-return();  probe ExceptionDescribe-entry(void*);    probe ExceptionDescribe-return();  probe ExceptionOccurred-entry(void*);  probe ExceptionOccurred-return(void*);  probe FatalError-entry(void* env, const char*);  probe FindClass-entry(void*, const char*);  probe FindClass-return(void*);  probe FromReflectedField-entry(void*, void*);  probe FromReflectedField-return(uintptr_t);  probe FromReflectedMethod-entry(void*, void*);  probe FromReflectedMethod-return(uintptr_t);  probe GetArrayLength-entry(void*, void*);  probe GetArrayLength-return(uintptr_t);  probe GetBooleanArrayElements-entry(void*, void*, uintptr_t*);  probe GetBooleanArrayElements-return(uintptr_t*);  probe GetBooleanArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, uintptr_t*);  probe GetBooleanArrayRegion-return();  probe GetBooleanField-entry(void*, void*, uintptr_t);  probe GetBooleanField-return(uintptr_t);  probe GetByteArrayElements-entry(void*, void*, uintptr_t*);  probe GetByteArrayElements-return(char*);  probe GetByteArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, char*);  probe GetByteArrayRegion-return();  probe GetByteField-entry(void*, void*, uintptr_t);  probe GetByteField-return(char);  probe GetCharArrayElements-entry(void*, void*, uintptr_t*);  probe GetCharArrayElements-return(uint16_t*);  probe GetCharArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, uint16_t*);  probe GetCharArrayRegion-return();  probe GetCharField-entry(void*, void*, uintptr_t);  probe GetCharField-return(uint16_t);  probe GetCreatedJavaVMs-eintptr_t*);  probe GetCreatedJavaVMs-return(uintptr_t);  probe GetCreateJavaVMs-entry(void*, uintptr_t, uintptr_t*);  probe GetCreateJavaVMs-return(uint32_t);  probe GetDefaultJavaVMInitArgs-entry(void*);  probe GetDefaultJavaVMInitArgs-return(uint32_t);  probe GetDirectBufferAddress-entry(void*, void*);  probe GetDirectBufferAddress-return(void*);  probe GetDirectBufferCapacity-entry(void*, void*);  probe GetDirectBufferCapacity-return(uintptr_t);  probe GetDoubleArrayElements-entry(void*, void*, uintptr_t*);  probe GetDoubleArrayElements-return(double*);  probe GetDoubleArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, double*);  probe GetDoubleArrayRegion-return();  probe GetDoubleField-entry(void*, void*, uintptr_t);  probe GetDoubleField-return(double);  probe GetEnv-entry(void*, void*, void*);  probe GetEnv-return(uint32_t);  probe GetFieldID-entry(void*, void*, const char*, const char*);  probe GetFieldID-return(uintptr_t);  probe GetFloatArrayElements-entry(void*, void*, uintptr_t*);  probe GetFloatArrayElements-return(float*);  probe GetFloatArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, float*);  probe GetFloatArrayRegion-return();  probe GetFloatField-entry(void*, void*, uintptr_t);  probe GetFloatField-return(float);  probe GetIntArrayElements-entry(void*, void*, uintptr_t*);  probe GetIntArrayElements-return(uint32_t*);  probe GetIntArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, uint32_t*);  probe GetIntArrayRegion-return();  probe GetIntField-entry(void*, void*, uintptr_t);  probe GetIntField-return(uint32_t);  probe GetJavaVM-entry(void*, void**);  probe GetJavaVM-return(uint32_t);  probe GetLongArrayElements-entry(void*, void*, uintptr_t*);  probe GetLongArrayElements-return(uintptr_t*);  probe GetLongArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, uintptr_t*);  probe GetLongArrayRegion-return();  probe GetLongField-entry(void*, void*, uintptr_t);  probe GetLongField-return(uintptr_t);  probe GetMethodID-entry(void*, void*, const char*, const char*);  probe GetMethodID-return(uintptr_t);  probe GetObjectArrayElement-entry(void*, void*, uintptr_t);  probe GetObjectArrayElement-return(void*);  probe GetObjectClass-entry(void*, void*);  probe GetObjectClass-return(void*);  probe GetObjectField-entry(void*, void*, uintptr_t);  probe GetObjectField-return(void*);  probe GetObjectRefType-entry(void*, void*);  probe GetObjectRefType-return(void*);  probe GetPrimitiveArrayCritical-entry(void*, void*, uintptr_t*);  probe GetPrimitiveArrayCritical-return(void*);  probe GetShortArrayElements-entry(void*, void*, uintptr_t*);  probe GetShortArrayElements-return(uint16_t*);  probe GetShortArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, uint16_t*);  probe GetShortArrayRegion-return();  probe GetShortField-entry(void*, void*, uintptr_t);  probe GetShortField-return(uint16_t);  probe GetStaticBooleanField-entry(void*, void*, uintptr_t);  probe GetStaticBooleanField-return(uintptr_t);  probe GetStaticByteField-entry(void*, void*, uintptr_t);  probe GetStaticByteField-return(char);  probe GetStaticCharField-entry(void*, void*, uintptr_t);  probe GetStaticCharField-return(uint16_t);  probe GetStaticDoubleField-entry(void*, void*, uintptr_t);  probe GetStaticDoubleField-return(double);  probe GetStaticFieldID-entry(void*, void*, const char*, const char*);  probe GetStaticFieldID-return(uintptr_t);  probe GetStaticFloatField-entry(void*, void*, uintptr_t);  probe GetStaticFloatField-return(float);  probe GetStaticIntField-entry(void*, void*, uintptr_t);  probe GetStaticIntField-return(uint32_t);  probe GetStaticLongField-entry(void*, void*, uintptr_t);  probe GetStaticLongField-return(uintptr_t);  probe GetStaticMethodID-entry(void*, void*, const char*, const char*);  probe GetStaticMethodID-return(uintptr_t);  probe GetStaticObjectField-entry(void*, void*, uintptr_t);  probe GetStaticObjectField-return(void*);  probe GetStaticShortField-entry(void*, void*, uintptr_t);  probe GetStaticShortField-return(uint16_t);  pro GetStringChars-entry(void*, void*, uintptr_t*);  probe GetStringChars-return(const uint16_t*);  probe GetStringCritical-entry(void*, void*, uintptr_t*);  probe GetStringCritical-return(const uint16_t*);  probe GetStringLength-entry(void*, void*);  probe GetStringLength-return(uintptr_t);  probe GetStringRegion-entry(void*, void*, uintptr_t, uintptr_t, uint16_t*);  probe GetStringRegion-return();  probe GetStringUTFChars-entry(void*, void*, uintptr_t*);  probe GetStringUTFChars-return(const char*);  probe GetStringUTFLength-entry(void*, void*);  probe GetStringUTFLength-return(uintptr_t);  probe GetStringUTFRegion-entry(void*, void*, uintptr_t, uintptr_t, char*);  probe GetStringUTFRegion-return();  probe GetSuperclass-entry(void*, void*);  probe GetSuperclass-return(void*);  probe GetVersion-entry(void*);  probe GetVersion-return(uint32_t);  probe IsAssignableFrom-entry(void*, void*, void*);  probe IsAssignableFrom-return(uintptr_t);  probe IsInstanceOf-entry(void*, void*, void*);  probe IsInstanceOf-return(uintptr_t);  probe IsSameObject-entry(void*, void*, void*);  probe IsSameObject-return(uintptr_t);  probe MonitorEnter-entry(void*, void*);  probe MonitorEnter-return(uint32_t);  probe MonitorExit-entry(void*, void*);  probe MonitorExit-return(uint32_t);  probe NewBooleanArray-entry(void*, uintptr_t);  probe NewBooleanArray-return(void*);  probe NewByteArray-entry(void*, uintptr_t);  probe NewByteArray-return(void*);  probe NewCharArray-entry(void*, uintptr_t);  probe NewCharArray-return(void*);  probe NewDirectByteBuffer-entry(void*, void*, uintptr_t);  probe NewDirectByteBuffer-return(void*);  probe NewDoubleArray-entry(void*, uintptr_t);  probe NewDoubleArray-return(void*);  probe NewFloatArray-entry(void*, uintptr_t);  probe NewFloatArray-return(void*);  probe NewGlobalRef-entry(void*, void*);  probe NewGlobalRef-return(void*);  probe NewIntArray-entry(void*, uintptr_t);  probe NewIntArray-return(void*);  probe NewLocalRef-entry(void*, void*);  probe NewLocalRef-return(void*);  probe NewLongArray-entry(void*, uintptr_t);  probe NewLongArray-return(void*);  probe NewObjectA-entry(void*, void*, uintptr_t);    probe NewObjectA-return(void*);  probe NewObjectArray-entry(void*, uintptr_t, void*, void*);  probe NewObjectArray-return(void*);  probe NewObject-entry(void*, void*, uintptr_t);   probe NewObject-return(void*);  probe NewObjectV-entry(void*, void*, uintptr_t);    probe NewObjectV-return(void*);  probe NewShortArray-entry(void*, uintptr_t);  probe NewShortArray-return(void*);  probe NewString-entry(void*, const uint16_t*, uintptr_t);  probe NewString-return(void*);  probe NewStringUTF-entry(void*, const char*);  probe NewStringUTF-return(void*);  probe NewWeakGlobalRef-entry(void*, void*);  probe NewWeakGlobalRef-return(void*);  probe PopLocalFrame-entry(void*, void*);  probe PopLocalFrame-return(void*);  probe PushLocalFrame-entry(void*, uint32_t);  probe PushLocalFrame-return(uint32_t);  probe RegisterNatives-entry(void*, void*, const void*, uint32_t);    probe RegisterNatives-return(uint32_t);  probe ReleaseBooleanArrayElements-entry(void*, void*, uintptr_t*, uint32_t);  probe ReleaseBooleanArrayElements-return();  probe ReleaseByteArrayElements-entry(void*, void*, char*, uint32_t);  probe ReleaseByteArrayElements-return();  probe ReleaseCharArrayElements-entry(void*, void*, uint16_t*, uint32_t);  probe ReleaseCharArrayElements-return();  probe ReleaseDoubleArrayElements-entry(void*, void*, double*, uint32_t);  probe ReleaseDoubleArrayElements-return();  probe ReleaseFloatArrayElements-entry(void*, void*, float*, uint32_t);  probe ReleaseFloatArrayElements-return();  probe ReleaseIntArrayElements-entry(void*, void*, uint32_t*, uint32_t);  probe ReleaseIntArrayElements-return();  probe ReleaseLongArrayElements-entry(void*, void*, uintptr_t*, uint32_t);  probe ReleaseLongArrayElements-return();  probe ReleaseObjectArrayElements-entry(void*, void*, void**, uint32_t);  probe ReleaseObjectArrayElements-return();  probe Releasey(void*, void*, void*, uint32_t);  probe ReleasePrimitiveArrayCritical-return();  probe ReleaseShortArrayElements-entry(void*, void*, uint16_t*, uint32_t);  probe ReleaseShortArrayElements-return();  probe ReleaseStringChars-entry(void*, void*, const uint16_t*);  probe ReleaseStringChars-return();  probe ReleaseStringCritical-entry(void*, void*, const uint16_t*);  probe ReleaseStringCritical-return();  probe ReleaseStringUTFChars-entry(void*, void*, const char*);  probe ReleaseStringUTFChars-return();  probe SetBooleanArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const uintptr_t*);  probe SetBooleanArrayRegion-return();  probe SetBooleanField-entry(void*, void*, uintptr_t, uintptr_t);  probe SetBooleanField-return();  probe SetByteArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const char*);  probe SetByteArrayRegion-return();  probe SetByteField-entry(void*, void*, uintptr_t, char);  probe SetByteField-return();  probe SetCharArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const uint16_t*);  probe SetCharArrayRegion-return();  probe SetCharField-entry(void*, void*, uintptr_t, uint16_t);  probe SetCharField-return();  probe SetDoubleArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const double*);  probe SetDoubleArrayRegion-return();  probe SetDoubleField-entry(void*, void*, uintptr_t, double);  probe SetDoubleField-return();  probe SetFloatArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const float*);  probe SetFloatArrayRegion-return();  probe SetFloatField-entry(void*, void*, uintptr_t, float);  probe SetFloatField-return();  probe SetIntArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const uint32_t*);  probe SetIntArrayRegion-return();  probe SetIntField-entry(void*, void*, uintptr_t, uint32_t);  probe SetIntField-return();  probe SetLongArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const uintptr_t*);  probe SetLongArrayRegion-return();  probe SetLongField-entry(void*, void*, uintptr_t, uintptr_t);  probe SetLongField-return();  probe SetObjectArrayElement-entry(void*, void*, uintptr_t, void*);  probe SetObjectArrayElement-return();  probe SetObjectField-entry(void*, void*, uintptr_t, void*);  probe SetObjectField-return();  probe SetShortArrayRegion-entry(void*, void*, uintptr_t, uintptr_t, const uint16_t*);  probe SetShortArrayRegion-return();  probe SetShortField-entry(void*, void*, uintptr_t, uint16_t);  probe SetShortField-return();  probe SetStaticBooleanField-entry(void*, void*, uintptr_t, uintptr_t);  probe SetStaticBooleanField-return();  probe SetStaticByteField-entry(void*, void*, uintptr_t, char);  probe SetStaticByteField-return();  probe SetStaticCharField-entry(void*, void*, uintptr_t, uint16_t);  probe SetStaticCharField-return();  probe SetStaticDoubleField-entry(void*, void*, uintptr_t, double);  probe SetStaticDoubleField-return();  probe SetStaticFloatField-entry(void*, void*, uintptr_t, float);  probe SetStaticFloatField-return();  probe SetStaticIntField-entry(void*, void*, uintptr_t, uint32_t);  probe SetStaticIntField-return();  probe SetStaticLongField-entry(void*, void*, uintptr_t, uintptr_t);  probe SetStaticLongField-return();  probe SetStaticObjectField-entry(void*, void*, uintptr_t, void*);  probe SetStaticObjectField-return();  probe SetStaticShortField-entry(void*, void*, uintptr_t, uint16_t);  probe SetStaticShortField-return();  probe Throw-entry(void*, void*);  probe ThrowNew-entry(void*, void*, const char*);    probe ThrowNew-return(uint32_t);  probe Throw-return(uint32_t);  probe ToReflectedField-entry(void*, void*, uintptr_t, uintptr_t);  probe ToReflectedField-return(void*);  probe ToReflectedMethod-entry(void*, void*, uintptr_t, uintptr_t);  probe ToReflectedMethod-return(void*);  probe UnregisterNatives-entry(void*, void*);    probe UnregisterNatives-return(uint32_t);};

致命錯誤報告

致命錯誤是諸如本機內存耗盡、內存訪問錯誤或指向進程的顯式信號等錯誤。應用程序中的本機代碼(例如,開發人員編寫的 Java 本機接口 (JNI) 代碼)、應用程序或 JVM 使用的第三方本機庫或 JVM 中的本機代碼可能會觸發致命錯誤. 如果致命錯誤導致託管 JVM 的進程終止,則 JVM 會收集有關錯誤的信息並寫入崩潰報告。Fatal errors are errors such as native memory exhaustion, memory access errors, or explicit signals directed to the process. Fatal errors can be triggered by native code within the application (for example, developer-written Java Native Interface (JNI) code), by third-party native libraries that the are used by application or the JVM, or by native code in the JVM. If a fatal error causes the process that is hosting the JVM to terminate, the JVM gathers information about the error and writes a crash report.

JVM 嘗試識別錯誤的性質和位置。如果可能,JVM 會在崩潰時寫入有關 JVM 和進程狀態的詳細信息。可用的詳細信息取決於平臺和崩潰的性質。此錯誤報告機制提供的信息可讓您更輕鬆、更高效地調試應用程序,並幫助您識別第三方代碼中的問題。當錯誤消息表明 JVM 代碼存在問題時,您可以提交更準確、更有幫助的錯誤報告。在某些情況下,崩潰報告生成會導致導致無法報告完整詳細信息的次要錯誤。The JVM tries to identify the nature and location of the error. If possible, the JVM writes detailed information about the state of the JVM and the process, at the time of the crash. The details that are available can depend on the platform and the nature of the crash. The information that is provided by this error-reporting mechanism lets you debug your application more easily and efficiently, and helps you identify issues in third-party code. When an error message indicates a problem in the JVM code, you can submit a more accurate and helpful bug report. In some cases, crash report generation causes secondary errors that prevent full details from being reported.

錯誤報告示例

以下示例顯示hs_err_pid18240.log了應用程序本機 JNI 代碼崩潰的錯誤報告(文件)的開頭:

## A fatal error has been detected by the Java Runtime Environment:##  SIGSEGV (0xb) at pc=0x00007f0f159f857d, pid=18240, tid=18245## JRE version: Java(TM) SE Runtime Environment (9.0+167) (build 9-ea+167)# Java VM: Java HotSpot(TM) 64-Bit Server VM (9-ea+167, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)# Problematic frame:# C  [libMyApp.so+0x57d]  Java_MyApp_readData+0x11## Core dump will be written. Default location: /cores/core.18240)## If you would like to submit a bug report, please visit:#   http://bugreport.java.com/bugreport/crash.jsp# The crash happened outside the Java Virtual Machine in native code.# See problematic frame for where to report the bug.# ---------------  S U M M A R Y ------------ Command Line: MyApp Host: Intel(R) Xeon(R) CPU           X5675  @ 3.07GHz, 24 cores, 141G, Ubuntu 12.04 LTSTime: Fri Apr 28 02:57:13 2017 EDT elapsed time: 2 seconds (0d 0h 0m 2s) ---------------  T H R E A D  --------------- Current thread (0x00007f102c013000):  JavaThread "main" [_thread_in_native, id=18245, stack(0x00007f10345c0000,0x00007f10346c0000)] Stack: [0x00007f10345c0000,0x00007f10346c0000],  sp=0x00007f10346be930,  free space=1018kNative frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)C  [libMyApp.so+0x57d]  Java_MyApp_readData+0x11j  MyApp.readData()I+0j  MyApp.main([Ljava/lang/String;)V+15v  ~StubRoutines::call_stubV  [libjvm.so+0x839eea]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x47aV  [libjvm.so+0x896fcf]  jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .isra.90]+0x21fV  [libjvm.so+0x8a7f1e]  jni_CallStaticVoidMethod+0x14eC  [libjli.so+0x4142]  JavaMain+0x812C  [libpthread.so.0+0x7e9a]  start_thread+0xda Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)j  MyApp.readData()I+0j  MyApp.main([Ljava/lang/String;)V+15v  ~StubRoutines::call_stub siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章