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

 

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