java虚拟机技术概述

123 阅读21分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

java虚拟机技术概述

Java虚拟机(JVM)的实现以及 java HotSpot 技术的主要功能

  • 自适应编译器:标准解释器用于启动应用程序,当应用程序运行时,将分析代码以检测性能 瓶颈或热点。 java HotSpot VM 编译代码中性能关键部分以提高性能, 但不编译很少使用的代码(大多数应用程序)。java HotSpot VM 使用自适应编译器来决定如何使用内联等技术优化已编译的代码。
  • 快速内存分配和垃圾回收:Java HotSpot 技术为对象提供快速内存分配,并提供快速、高效、最先进的垃圾回收器。
  • 线程同步:Java HotSpot 技术提供了一种线程处理功能,旨在扩展以用于大型共享内存多出处理器服务器。

编译器控制

编译器控制提供一种通过编译器指令选项控制 java虚拟机(JVM) 编译的方法。控制级别是可管理的,并且是特定于方法的。

编译器指令是告诉JVM应如何进行编译的指令。指令在控制编译过程时提过方法上下文精度。

编写指令

编译器控制选项

选项是编译说明。选项提供方法上下文精度。可用选项因编译器而异,并且需要特定类型的值。

表2-1 常用选项

选择描述值类型默认值
Enable隐藏指令,如果该指令设置为 , 则使其不匹配。此选项对于防止选项重复很有用。falsebooltrue
Exclude从编译中排除方法。boolfalse
BreakAtExecute设置断点以在调试 JVM 时在指定方法的开头停止执行。boolfalse
BreakAtCompile设置断点以在调试 JVM 时在指定方法的开头停止编译。boolfalse
Log在日志中仅放置指定的方法。必须首先设置命令行选项 。默认值将所有已编译的方法放在日志中。-XX:+LogCompilation``falseboolfalse
PrintAssembly使用外部库打印字节编码方法和本机方法的程序集代码。disassembler.soboolfalse
PrintInlining打印哪些方法内联,以及嵌入的位置。boolfalse
PrintNMethods在生成信息时打印它们。boolfalse
BackgroundCompilation将方法编译为后台任务。方法在解释器模式下运行,直到后台编译完成。该值将方法编译为前台任务。falsebooltrue
ReplayInline启用与相应的全局选项相同的功能,但基于每个方法。CIReplayboolfalse
DumpReplay启用与相应的全局选项相同的功能,但基于每个方法。CIReplayboolfalse
DumpInline启用与相应的全局选项相同的功能,但基于每个方法。CIReplayboolfalse
CompilerDirectivesIgnoreCompileCommands忽略所有编译命令。boolfalse
DisableIntrinsic禁止使用基于方法匹配条件的内部函数。ccstr无默认值。
inline强制或阻止基于方法匹配条件内联方法。ccstr[]无默认值。

表 2-2 C2 独占选项

选择描述值类型默认值
BlockLayoutByFrequency从不频繁的执行分支从热路径移动。booltrue
PrintOptoAssembly使用外部库在编译后打印生成的程序集代码。这需要对 JVM 进行调试构建。disassembler.soboolfalse
PrintIntrinsics打印使用哪些固有方法以及使用位置。boolfalse
TraceOptoPipelining跟踪流水线信息,类似于相应的全局选项,但基于每个方法。这适用于慢速和快速调试生成。boolfalse
TraceOptoOutput跟踪流水线信息,类似于相应的全局选项,但基于每个方法。这适用于慢速和快速调试生成。boolfalse
TraceSpilling跟踪变量溢出。boolfalse
Vectorize跨矢量寄存器并行执行计算。boolfalse
VectorizeDebug跨矢量寄存器并行执行计算。这需要对 JVM 进行调试构建。intx0
CloneMapDebug使您能够检查从矢量化生成的。这需要对 JVM 进行调试构建。CloneMapboolfalse
IGVPrintLevel指定在 Oracle 的热点理想图形可视化工具 (IGV) 中打印编译器图形的点。值越高意味着粒度越高。intx0
MaxNodeLimit设置在单个方法的编译期间要使用的最大节点数。intx80000

编写指令文件

各个编译器指令都写在指令文件中。只有指令文件(而不是单个指令)可以添加到活动指令堆栈中。

  1. 创建扩展名为.json 的文件。指令文件是使用 JSON 语法的子集编写的,但有少量的添加和偏差。

  2. 将以下语法添加为可从中工作的模板:

    Copy[  //Array of Directives
        {   //Directive Block
            //Directive 1
        },
        {   //Directive Block
            //Directive 2
        },
    ]
    

    此模板的组件包括:

    指令数组

    • 指令文件存储一个指令块数组,用一对方括号 [] 表示。
    • 如果文件仅包含单个指令块,则括号是可选的。

    指令块

    • 块用一对大括号 {} 表示。
    • 一个块包含一个单独的指令。
    • 指令文件可以包含任意数量的指令块。
    • 块用逗号 , 分隔。
    • 逗号是数组中最后一个块后面的可选。

    命令

    • 每个指令都必须位于一个指令块中。
    • 当指令文件包含多个指令块时,它可以包含多个指令。

    评论

    • 单行注释前面有两个斜杠 //
    • 不允许使用多行注释。
  3. 在模板中添加或删除指令块,以匹配指令文件中所需的指令数。

  4. 在每个指令块中,编写一个编译器指令。

  5. 如有必要,请对指令块重新排序。文件中指令的顺序非常重要。写在靠近阵列开头的指令获得更高的优先级。

下面的示例演示一个包含两个编译器指令的已完成指令文件:

Copy[  //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. 插入以下代码块(作为可从中工作的模板)来编写单个编译器指令。此代码块是一个指令块。

        {
            match: [],
            c1: {
                //c1 directive options
            },
            c2: {
                //c2 directive options
            },
            //Directive options applicable to all compilers
        },
    
  2. match属性提供方法模式数组。

    例如:

    Copy        match: ["java*.*", "oracle*.*"],
    
  3. c1属性提供逗号分隔的指令选项块。确保这些选项对 c1 编译器有效。

    例如:

    Copy        c1: {
                Enable: true,
                Exclude: true,
                BreakAtExecute: true,
            },
    
  4. c2属性提供逗号分隔的指令选项块。此块可以包含常见和 c2 独占编译器选项的混合。

    例如:

    Copy        c2: {
                Enable: false,
                MaxNodeLimit: 1000,
            },
    
  5. 在指令末尾提供您希望适用于所有编译器的选项。这些选项被视为在公共块的范围内编写。选项以逗号分隔。

    例如:

    Copy        BreakAtCompile: true,
            DumpReplay: true,
    
  6. 通过完成以下步骤清理文件。

    1. 检查指令选项是否重复。如果发生冲突,则最后一次出现的选项优先。冲突通常发生在公共块和 c1 或 c2 块之间,而不是 c1 和 c2 块之间。
    2. 避免在公共块中写入 c2 独占指令选项。尽管公共块可以接受公共和 c2 独占选项的混合,但以这种方式构建指令毫无意义,因为公共块中的 c2 独占选项对 c1 编译器没有影响。改为在 c2 块中写入 c2 独占选项。
    3. 如果c1c2属性没有相应的指令选项,则省略该编译器的属性值语法以下示例显示基于前面示例的结果指令为:
Copy    {
        match: ["java*.*", "oracle*.*"],
        c1: {
            Enable: true,
            Exclude: true,
            BreakAtExecute: true,
        },
        c2: {
            Enable: false,
            MaxNodeLimit: 1000,
        },
        BreakAtCompile: true,
        DumpReplay: true,
    },

指令文件的 JSON 格式允许以下语法偏差:

  • 额外的尾随逗号在数组和对象中是可选的。
  • 属性是字符串,可以选择放在引号内。
  • 如果数组只包含一个元素,则括号是可选的。

因此,下面的示例演示有效的编译器指令:

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

在编译器指令中编写方法模式

A 是一种方法模式,您可以精确地编写它,也可以使用通配符进行泛化。您可以指定哪些最匹配的 Java 代码应应用随附的指令选项,或者应内联哪些 Java 代码。ccstr

编写方法模式:

  1. 使用以下语法编写方法模式:package/class.method(parameter_list)。若要使用通配符概括方法模式,请参阅步骤 2。

    下面的示例演示使用此语法的方法模式:

    Copyjava/lang/String.indexOf()
    

    其他格式样式也可用。这确保了与早期方法匹配方法(如编译命令)的向后兼容性。上一示例的有效格式替代项包括:

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

    最后一个格式样式与热点输出匹配。

  2. 插入通配符 (*),在其中要泛化方法模式的一部分。

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

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

    增大的泛化会导致精度降低。更多的 Java 代码成为与方法模式的潜在匹配。因此,明智地使用通配符 () 非常重要。*

  3. 根据 Java 规范修改方法模式的签名部分。签名匹配必须精确,否则签名默认为通配符 (*)。省略的签名也默认为通配符。签名不能包含通配符。

  4. **可选:**如果编写方法模式以随内联指令选项一起使用,则必须在方法模式前面加上其他字符。

编写内联指令选项

指令选项的属性需要一个带有特殊命令前缀的方法模式数组。这指示哪些方法模式应该或不应该内联。inline

  1. 内联写:在指令的公共块、c1 块或 c2 块中。
  2. 添加一个精心排序的方法模式数组。执行第一个匹配方法模式上的前缀命令。数组中的其余方法模式将被忽略。
  3. 前缀 a +以强制内联任何匹配的 Java 代码。
  4. 前缀 a -以防止内联任何匹配的 Java 代码。
  5. **可选:**如果需要将内联行为应用于多个方法模式,请重复步骤 1 到 4 以编写多个内联语句。不要编写包含多个方法模式的单个数组。

以下示例显示了指令选项:inline

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

使用启用选项防止重复

可以使用该选项隐藏指令的各个方面并防止指令之间的重复。Enable

在下面的示例中,编译器指令的属性是相同的。:下面的示例演示如何使用该选项解决不需要的代码重复。 隐藏块指令并使其不可匹配。通常,第一个匹配指令应用于方法的编译。该选项为此规则提供了例外。通常由第一个或第二个指令编译的方法现在使用第三个指令的块进行编译。第三个指令的块是不可访问的,因为第一个和第二个指令中的块优先。c1

Copy[
    {
        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
Copy[
    {
        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
        },
    },
]

了解指令

什么是默认指令?

默认指令是包含所有可能的指令选项的默认值的编译器指令。它是堆栈中最底层的指令,与提交编译的每个方法匹配。

设计新的编译器指令时,可以指定新指令与默认指令的不同之处。默认指令将成为指导设计决策的模板。

默认指令中的指令选项值

您可以打印一个空的指令堆栈,以显示默认编译器指令中所有指令选项的匹配条件和值:

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

注意:某些选项仅适用于编译器。有关完整列表,请参阅表 2-2c2

新指令中的指令选项值

在新指令中,必须指定指令与默认指令的区别。如果未指定指令选项,则该选项将保留默认指令中的值。

示例:当您向指令堆栈添加新指令时,默认指令将成为堆栈中最底部的指令。对于此示例,当您打印指令堆栈时,它显示了新指令中指定的指令选项与默认指令中的值有何不同:

[
    {
        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 

如何将指令应用于代码?

指令应用于基于方法匹配过程的代码。提交进行编译的每个方法都与指令堆栈中的指令匹配。

将方法与指令堆栈中的指令进行匹配的过程由编译器经纪人执行。

方法匹配过程

提交方法进行编译时,会将该方法的完全限定名称与指令堆栈中的匹配条件进行比较。堆栈中匹配的第一个指令将应用于该方法。堆栈中的其余指令将被忽略。如果未找到匹配项,则应用默认指令。

对编译中的所有方法重复此过程。可以在编译中应用多个指令,但每个方法只能应用一个指令。堆栈中的所有指令都被视为活动指令,因为它们可能适用。活动指令和应用指令之间的主要区别在于:

  • 如果指令堆栈中存在指令,则该指令处于活动状态。
  • 如果指令影响代码,则会应用该指令。

示例 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) 等探查器来测量所应用指令的累积效应。
  • 编译器经纪人会忽略创建错误代码的指令选项,例如在不支持的平台上强制执行硬件指令。将显示一条警告消息。
  • 指令选项具有与典型命令行标志相同的限制。例如,仅当中间表示 (IR) 未变得太大时,才会遵循内联代码的说明。

编译器控制和向后兼容性

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

用于处理指令文件的命令

编译器指令和命令行

可以使用命令行界面在启动程序时添加和打印编译器指令。

您只能在命令行中指定一个指令文件。该文件中的所有指令都将添加到指令堆栈中,并在程序启动时立即处于活动状态。通过在命令行中添加指令,您可以在程序的早期阶段测试指令的性能影响。您还可以专注于调试和开发程序。

通过命令行添加指令

以下命令行选项指定指令文件:启动 Java 程序时包括此命令行选项。下面的示例显示了此选项,该选项以 :在示例中启动:

XX:CompilerDirectivesFile=file

TestProgram

java -XX:+UnlockDiagnosticVMOptions -XX:CompilerDirectivesFile=File_A.json TestProgram
  • -XX:+UnlockDiagnosticVMOptions启用诊断选项。在命令行添加指令之前,必须输入此命令。
  • -XX:CompilerDirectivesFile是一种诊断选项。您可以使用它来指定一个要添加到指令堆栈中的指令文件。
  • File_A.json是一个指令文件。该文件可以包含多个指令,所有这些指令在程序启动时都会添加到活动指令的堆栈中。
  • 如果包含语法错误或格式错误的指令,则会显示一条错误消息,并且不会启动。File_A.json``TestProgram

通过命令行打印指令

您可以在程序启动或通过诊断命令添加其他指令时自动打印指令堆栈。以下命令行选项用于启用此行为:下面的示例演示如何在命令行中包含此诊断命令:

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

编译器指令和诊断命令

可以使用诊断命令来管理哪些指令在运行时处于活动状态。您可以添加或删除指令,而无需重新启动正在运行的程序。

制作单个完美的指令文件可能需要一些迭代和实验。诊断命令提供了强大的机制,用于测试指令堆栈中指令的不同配置。诊断命令允许您添加或删除指令,而无需重新启动正在运行的程序的 JVM。

获取 Java 进程标识号

要测试指令,必须找到正在运行的程序的处理器标识符 (PID) 编号。

  1. 打开终端。
  2. 输入jcmd命令。

该命令返回正在运行的 Java 进程的列表及其 PID 编号。在下面的示例中,返回的信息是:jcmd``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 在没有指令的情况下启动程序

在这里插入图片描述

您可以启动应用程序并指定指令。

  • 要启动应用程序并将指令从 添加到指令堆栈,请在命令行中输入以下命令:TestProgram``File_A.json

    Copyjava -XX:+UnlockDiagnosticVMOptions -XX:CompilerDirectivesFile=File_A.json TestProgram
    
  • TestProgram启动,并将 中的指令添加到堆栈中。指令文件中最上面的指令将成为指令堆栈中最上面的指令。File_A

  • 图 2-2显示堆栈中指令从上到下的顺序为 [1, 2, 0]。

图 2-2 使用指令启动程序

在这里插入图片描述

向正在运行的应用程序添加指令

可以通过诊断命令向正在运行的应用程序添加指令。

  • 要将 中的所有指令添加到指令堆栈,请输入以下命令:File_B

    Copyjcmd 11084 Compiler.directives_add File_B.json
    

    中的指令将添加到堆栈的顶部。File_B

  • 图 2-3显示堆栈中指令的顺序为 [3, 1, 2, 0]。

图 2-3 向正在运行的程序添加指令

在这里插入图片描述

您可以在指令运行时通过诊断命令将指令文件添加到:TestProgram

  • 要将 中的所有指令添加到指令堆栈,请输入以下命令。File_C

    Copyjcmd 11084 Compiler.directives_add File_C.json
    
  • 图 2-4显示堆栈中指令的顺序为 [4, 5, 3, 1, 2, 0]。

图 2-4 向正在运行的程序添加多个指令

在这里插入图片描述

从指令堆栈中删除指令

您可以通过诊断命令从指令堆栈中删除最顶层的指令。

  • 要从堆栈中删除,请输入以下命令:Directive 4

    Copyjcmd 11084 Compiler.directives_remove
    
  • 若要删除更多,请重复此诊断命令,直到只剩下默认指令。您无法删除默认指令。

  • 图 2-5显示堆栈中指令的顺序为 [5, 3, 1, 2, 0]。

图 2-5 从堆栈中删除一个指令

您可以从指令堆栈中删除多个指令。

  • 要清除指令堆栈,请输入以下命令:

    Copyjcmd 11084 Compiler.directives_clear
    
  • 除默认指令外,将删除所有指令。您无法删除默认指令。

  • 图 2-6显示仅保留在堆栈中。Directive 0

图 2-6 从堆栈中删除所有指令

在这里插入图片描述