`

JVM实用参数(二)参数分类和即时(JIT)编译器诊断

阅读更多

在这个系列的第二部分,我来介绍一下HotSpot JVM提供的不同类别的参数。我同样会讨论一些关于JIT编译器诊断的有趣参数。

JVM 参数分类

HotSpot JVM 提供了三类参数。第一类包括了标准参数。顾名思义,标准参数中包括功能和输出的参数都是很稳定的,很可能在将来的JVM版本中不会改变。你可以用java命令(或者是用 java -help)检索出所有标准参数。我们在第一部分中已经见到过一些标准参数,例如:-server。

第二类是X参数,非标准化的参数在将来的版本中可能会改变。所有的这类参数都以-X开始,并且可以用java -X来检索。注意,不能保证所有参数都可以被检索出来,其中就没有-Xcomp。

第三类是包含XX参数(到目前为止最多的),它们同样不是标准的,甚至很长一段时间内不被列出来(最近,这种情况有改变 ,我们将在本系列的第三部分中讨论它们)。然而,在实际情况中X参数和XX参数并没有什么不同。X参数的功能是十分稳定的,然而很多XX参数仍在实验当中(主要是JVM的开发者用于debugging和调优JVM自身的实现)。值的一读的介绍非标准参数的文档 HotSpot JVM documentation,其中明确的指出XX参数不应该在不了解的情况下使用。这是真的,并且我认为这个建议同样适用于X参数(同样一些标准参数也是)。不管类别是什么,在使用参数之前应该先了解它可能产生的影响。

用一句话来说明XX参数的语法。所有的XX参数都以”-XX:”开始,但是随后的语法不同,取决于参数的类型。

  • 对于布尔类型的参数,我们有”+”或”-”,然后才设置JVM选项的实际名称。例如,-XX:+<name>用于激活<name>选项,而-XX:-<name>用于注销选项。
  • 对于需要非布尔值的参数,如string或者integer,我们先写参数的名称,后面加上”=”,最后赋值。例如,  -XX:<name>=<value>给<name>赋值<value>。

现在让我们来看看JIT编译方面的一些XX参数。

-XX:+PrintCompilation and -XX:+CITime

当一个Java应用运行时,非常容易查看JIT编译工作。通过设置-XX:+PrintCompilation,我们可以简单的输出一些关于从字节码转化成本地代码的编译过程。我们来看一个服务端VM运行的例子:

$ java -server -XX:+PrintCompilation Benchmark
  1       java.lang.String::hashCode (64 bytes)
  2       java.lang.AbstractStringBuilder::stringSizeOfInt (21 bytes)
  3       java.lang.Integer::getChars (131 bytes)
  4       java.lang.Object::<init> (1 bytes)
---   n   java.lang.System::arraycopy (static)
  5       java.util.HashMap::indexFor (6 bytes)
  6       java.lang.Math::min (11 bytes)
  7       java.lang.String::getChars (66 bytes)
  8       java.lang.AbstractStringBuilder::append (60 bytes)
  9       java.lang.String::<init> (72 bytes)
 10       java.util.Arrays::copyOfRange (63 bytes)
 11       java.lang.StringBuilder::append (8 bytes)
 12       java.lang.AbstractStringBuilder::<init> (12 bytes)
 13       java.lang.StringBuilder::toString (17 bytes)
 14       java.lang.StringBuilder::<init> (18 bytes)
 15       java.lang.StringBuilder::append (8 bytes)
[...]
 29       java.util.regex.Matcher::reset (83 bytes)

每当一个方法被编译,就输出一行-XX:+PrintCompilation。每行都包含顺序号(唯一的编译任务ID)和已编译方法的名称和大小。因此,顺序号1,代表编译String类中的hashCode方法到原生代码的信息。根据方法的类型和编译任务打印额外的信息。例如,本地的包装方法前方会有”n”参数,像上面的System::arraycopy一样。注意这样的方法不会包含顺序号和方法占用的大小,因为它不需要编译为本地代码。同样可以看到被重复编译的方法,例如StringBuilder::append顺序号为11和15。输出在顺序号29时停止 ,这表明在这个Java应用运行时总共需要编译29个方法。

没有官方的文档关于-XX:+PrintCompilation,但是这个描述是对于此参数比较好的。我推荐更深入学习一下。

JIT编译器输出帮助我们理解客户端VM与服务端VM的一些区别。用服务端VM,我们的应用例子输出了29行,同样用客户端VM,我们会得到55行。这看起来可能很怪,因为服务端VM应该比客户端VM做了“更多”的编译。然而,由于它们各自的默认设置,服务端VM在判断方法是不是热点和需不需要编译时比客户端VM观察方法的时间更长。因此,在使用服务端VM时,一些潜在的方法会稍后编译就不奇怪了。

通过另外设置-XX:+CITime,我们可以在JVM关闭时得到各种编译的统计信息。让我们看一下一个特定部分的统计:

$ java -server -XX:+CITime Benchmark
[...]
Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  0.178 s
    Standard compilation   :  0.129 s, Average : 0.004
    On stack replacement   :  0.049 s, Average : 0.024
[...]

总共用了0.178s(在29个编译任务上)。这些,”on stack replacement”占用了0.049s,即编译的方法目前在堆栈上用去的时间。这种技术并不是简单的实现性能显示,实际上它是非常重要的。没有”on stack replacement”,方法如果要执行很长时间(比如,它们包含了一个长时间运行的循环),它们运行时将不会被它们编译过的副本替换。

再一次,客户端VM与服务端VM的比较是非常有趣的。客户端VM相应的数据表明,即使有55个方法被编译了,但这些编译总共用了只有0.021s。服务端VM做的编译少但是用的时间却比客户端VM多。这个原因是,使用服务端VM在生成本地代码时执行了更多的优化。

在本系列的第一部分,我们已经学了-Xint和-Xcomp参数。结合使用-XX:+PrintCompilation和-XX:+CITime,在这两个情况下(校对者注,客户端VM与服务端VM),我们能对JIT编译器的行为有更好的了解。使用-Xint,-XX:+PrintCompilation在这两种情况下会产生0行输出。同样的,使用-XX:+CITime时,证实在编译上没有花费时间。现在换用-Xcomp,输出就完全不同了。在使用客户端VM时会产生726行输出,然后没有更多的,这是因为每个相关的方法都被编译了。使用服务端VM,我们甚至能得到993行输出,这告诉我们更积极的优化被执行了。同样,JVM 拆机(JVM teardown)时打印出的统计显示了两个VM的巨大不同。考虑服务端VM的运行:

$ java -server -Xcomp -XX:+CITime Benchmark
[...]
Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  1.567 s
    Standard compilation   :  1.567 s, Average : 0.002
    On stack replacement   :  0.000 s, Average : -1.#IO
[...]

使用-Xcomp编译用了1.567s,这是使用默认设置(即,混合模式)的10倍。同样,应用程序的运行速度要比用混合模式的慢。相比较之下,客户端VM使用-Xcomp编译726个方法只用了0.208s,甚至低于使用-Xcomp的服务端VM。

补充一点,这里没有”on stack replacement”发生,因为每一个方法在第一次调用时被编译了。损坏的输出“Average: -1.#IO”(正确的是:0)再一次表明了,非标准化的输出参数不是非常可靠。

-XX:+UnlockExperimentalVMOptions

有些时候当设置一个特定的JVM参数时,JVM会在输出“Unrecognized VM option”后终止。如果发生了这种情况,你应该首先检查你是否输错了参数。然而,如果参数输入是正确的,并且JVM并不识别,你或许需要设置-XX:+UnlockExperimentalVMOptions 来解锁参数。我不是非常清楚这个安全机制的作用,但我猜想这个参数如果不正确使用可能会对JVM的稳定性有影响(例如,他们可能会过多的写入debug输出的一些日志文件)。

有一些参数只是在JVM开发时用,并不实际用于Java应用。如果一个参数不能被 -XX:+UnlockExperimentalVMOptions 开启,但是你真的需要使用它,此时你可以尝试使用debug版本的JVM。对于Java 6 HotSpot JVM你可以从这里找到

-XX:+LogCompilation and -XX:+PrintOptoAssembly

如果你在一个场景中发现使用 -XX:+PrintCompilation,不能够给你足够详细的信息,你可以使用 -XX:+LogCompilation把扩展的编译输出写到“hotspot.log”文件中。除了编译方法的很多细节之外,你也可以看到编译器线程启动的任务。注意-XX:+LogCompilation 需要使用-XX:+UnlockExperimentalVMOptions来解锁。

JVM甚至允许我们看到从字节码编译生成到本地代码。使用-XX:+PrintOptoAssembly,由编译器线程生成的本地代码被输出并写到“hotspot.log”文件中。使用这个参数要求运行的服务端VM是debug版本。我们可以研究-XX:+PrintOptoAssembly的输出,以至于了解JVM实际执行什么样的优化,例如,关于死代码的消除。一个非常有趣的文章提供了一个例子

分享到:
评论

相关推荐

    性能调优+JVM详解+JVM核心参数

    参数分类和JIT编译器诊断:标准参数、非标准参数、调试参数和JIT监控工具。 垃圾回收和内存调优:新生代和老年代的垃圾回收参数调优。 代码缓存:调整代码缓存大小和解决溢出问题的参数。 并行垃圾回收器:设置和...

    深入JVM内核—原理、诊断与优化视频教程-2.JVM运行机制

    5. **字节码执行引擎**:JVM的字节码解释器负责解释执行字节码,而即时编译器(JIT)会将热点代码编译为本地机器码,以提高执行效率。HotSpot JVM中的C1和C2编译器分别针对不同的性能需求进行优化。 6. **内存溢出...

    Java虚拟机-jvm故障诊断与性能优化-源码

    9. **JIT编译器** - **即时编译**:HotSpot JVM的C1和C2编译器将热点代码编译为原生机器码,提升执行效率。 - **编译触发条件**:如方法调用次数、代码执行时间等。 10. **JVM并发编程** - **线程池**:使用...

    实战JAVA虚拟机 (JVM故障诊断与性能优化)【含源码】

    书中还涉及了JVM的编译优化,包括即时编译(JIT)和动态优化。解释器与编译器的协同工作方式,热点探测机制,以及如何通过-XX选项调控编译策略,都是提升程序运行效率的关键知识点。 源码部分则让理论知识落地,...

    JVM学习资料+笔记

    2. 字节码执行引擎:JVM通过解释器和即时编译器(JIT)来执行字节码,解释器用于快速启动,JIT则在运行时优化代码性能。 3. 内存模型:JVM内存分为堆、栈、方法区、本地方法栈、程序计数器等几部分。其中,堆是对象...

    深入JVM内核原理、诊断与优化.zip

    7. **JIT编译器**:JVM包含解释器和即时编译器(如C1、C2)。JIT能够将热点代码编译为本地机器码,显著提升执行效率。了解JIT的工作原理和优化策略,可以进一步提升程序性能。 总的来说,《深入JVM内核原理、诊断与...

    jvm和gc详解及调优

    8. **JIT编译器**:介绍JVM的即时编译器(Just-In-Time Compiler),它将热点代码编译为机器码以提高执行效率。 9. **内存泄漏与性能瓶颈**:识别和解决内存泄漏问题,以及如何定位和解决性能瓶颈。 10. **实战...

    深入JVM内核-原理、诊断与优化ppt.zip

    字节码执行是JVM的另一大特色,通过解释器和即时编译器(JIT),JVM能够将字节码转换为高效的机器指令。理解字节码和JIT的工作原理,有助于提升代码的运行效率。 综上所述,《深入JVM内核——原理、诊断与优化》...

    JVM课件(云析学院JVM课程课件)

    在高级篇和优化篇中,虽然没有提供具体的内容,我们可以推断出,高级篇可能包含了对JVM更深层次的探讨,例如JVM内存模型的高级细节、类加载器的深入讨论、即时编译器(JIT)的工作原理等。而优化篇可能会涵盖JVM性能...

    Java虚拟机:JVM高级特性与最佳实践(第二版)

    《Java虚拟机:JVM高级特性与最佳实践(第二版)》是一本深入探讨Java虚拟机(JVM)的专业书籍,对于Java开发者来说,理解JVM的工作原理和优化技巧至关重要。JVM作为Java语言的核心组成部分,它负责运行Java程序,...

    java虚拟机深入JVM内核—原理、诊断与优化视频教程网盘下载

    3. 执行引擎:负责执行字节码,包括解释器和即时编译器(JIT)。 4. 本地方法接口:用于调用C/C++的本地方法。 5. 本地库支持:提供与操作系统交互的能力。 二、内存管理 1. 堆内存:存储对象实例,分为新生代和老...

    jvm.rar_jvm

    3. **字节码与机器码转换**:设计并实现字节码解释器或JIT编译器,将Java字节码转换为目标平台的机器码。 4. **系统调用适配**:实现本地方法接口,使JVM能与目标平台的系统服务交互。 5. **性能优化**:针对特定...

    JVM从0-1学习,掌握如何解决JVM相关问题

    8. **JIT编译器**:JVM在运行过程中,对于热点代码会进行即时编译,提高运行效率。理解JIT的工作机制和分层编译。 9. **线程模型**:JVM中的线程如何创建、调度和同步,熟悉synchronized、Lock、ThreadLocal等并发...

    jvm调优-jvm.zip

    7. **编译优化**:JVM有即时编译器(JIT),会将频繁执行的热点代码编译为原生机器码。`-XX:CompileThreshold`和`-XX:PreBlockCount`等参数可以影响编译策略。 8. **性能监控工具**:如JVisualVM、JConsole、JFR...

    全面理解JVM虚拟机.rar

    解释器逐条执行字节码,而JIT编译器则会将热点代码转换为本地机器码以提高执行速度。理解执行引擎的工作方式对于性能调优至关重要。 5. GC垃圾回收 垃圾回收(Garbage Collection, GC)是JVM自动管理内存的重要...

    监测JVM各项性能指标

    - **即时编译(JIT)**:JIT编译的性能直接影响运行效率,关注其编译时间和编译的热点方法。 7. **实战应用** - 使用`WatchTest`这样的测试工具来模拟实际场景,验证不同配置和调整对JVM性能的影响。 - 结合日志...

    Inside JVM CHM

    - 字节码执行引擎:解释器和即时编译器(JIT)如何协同工作,提高程序运行效率。 2. **内存管理**: - 堆内存:对象的生命周期、内存分配策略、内存回收(GC)等。 - 栈内存:线程私有的数据区域,存储基本类型...

    从 0 开始带你成为JVM实战高手

    2. 字节码执行:JVM通过解释器和即时编译器(如HotSpot的C1和C2编译器)将字节码转换为机器指令,理解这一过程对于优化代码性能至关重要。 3. 内存模型:JVM内存分为堆、栈、方法区、本地方法栈和程序计数器等几个...

    jvm优化的资源,和jvm调优上中下的介绍配套

    5. **编译优化**:JVM的即时编译器(JIT)将热点代码编译为本地机器码,提高执行效率。了解并优化HotSpot的编译策略(例如,方法的进入次数阈值)可进一步提升性能。 6. **监控与诊断工具**:如VisualVM、JProfiler...

Global site tag (gtag.js) - Google Analytics