BestHotSpotJVMOptionsandSwitchesfromJava11toJava17适应您的程序和操作环境。HotSpotJVM是一项了不起的灵活技术。它作为每个主要操作系统和CPU架构的二进制版本出现,从微型RaspberryPiZeros一直到包含数百个CPU内核和TBRAM的“大型”服务器。由于OpenJDK是一个开源项目,HotSpotJVM几乎可以针对任何其他系统进行编译,并且可以使用选项、开关和标志对其进行微调。首先,这里有一些背景。HotSpotJVM的语言是字节码。在撰写本文时,有超过30种编程语言可以编译为HotSpotJVM兼容的字节码,但到目前为止,在全球拥有超过800万开发人员的最流行的当然是Java。使用javac编译器将Java源代码编译成类文件形式的字节码(如图1所示)。在现代开发中,这可能会被Maven、Gradle或基于IDE的编译器等构建工具抽象掉。图1.编译字节码的过程程序的字节码表示由HotSpotJVM在虚拟堆栈机器上执行,该机器知道多达256条不同的指令,每条指令都由一个8位数字操作码标识;因此得名“字节码”。字节码程序由解释器执行,解释器获取每条指令,将其操作数压入堆栈,执行指令,移除操作数并将结果留在堆栈上,如图2所示。图2.堆栈上的结果解释器执行字节码,从底层环境中抽象出程序执行,赋予Java“一次编写,随处运行”的可移植性优势。在一种架构上编译的类文件可以在完全不同架构的HotSpotJVM上执行。如果您认为底层硬件的这种抽象是以牺牲性能为代价的,那么您是对的。这通常是HotSpotJVM开关、选项和标志发挥作用的地方。2.JIT即时编译用可移植的、功能丰富的高级语言(如Java)编写的程序如何挑战那些从“低级”、“不太友好”的编程语言(如C)架构特定的native代码程序的性能如何?答案是HotSpotJVM包括提高性能的即时(JIT)编译技术,它分析程序执行并有选择地优化它认为最有利的部分。这些被称为程序的热点(因此得名HotSpotJVM),它通过使用底层系统架构的知识将它们动态编译成本机代码来实现这一点。HotSpotJVM包括两个JIT编译器,称为C1(客户端编译器)和C2(服务器编译器),它们提供不同的优化权衡。C1提供快速简单的优化。C2提供高级优化,需要更多分析并且应用成本更高。自JDK8发布以来,默认行为一直是在称为分层编译的模式下同时使用两个编译器,其中C1提供快速提升,而C2在进行高级优化之前收集足够的配置文件信息。生成的native代码存放在热点JVM的内存区域,称为代码缓存,如图3所示。图3.Java编译过程三。GCGarbageCollection除了JIT技术,HotSpotJVM还包括提高生产力和性能的特性,例如:多线程和自动内存管理以及垃圾收集(GC)策略的选择。对象被分配在HotSpotJVM的一块内存区域,称为堆,一旦这些对象不再被引用,垃圾收集器就可以清理它们并回收它们使用的内存。4.符合人体工程学的HotSpotJVMHotSpotJVM具有如此多的灵活性和动态行为,以至于您可能担心如何配置它才能最好地满足您的程序要求。幸运的是,对于许多用例,您不需要进行任何手动调整。HotSpotJVM包括一个称为人机工程学(ergonomics)的过程,它会在启动时检查执行环境,并根据CPU核心数和可用RAM量为GC策略、堆大小和JIT编译器选择一些合理的默认值。当前的默认值是:垃圾收集器:G1GC初始堆:物理内存的1/64最大堆:物理内存的1/4JIT编译器:使用选项-XX:+PrintFlagsFinal使用C1和C2进行分层编译并搜索_ergonomic_使用grep命令,您可以看到HotSpotJVM将为您的环境选择的所有符合人体工程学的默认值,如下所示:java-XX:+PrintFlagsFinal|Grep人体工程学IntxcicompilerCountU=4UInt{product}{entronogic}concgcthreads=2{product}{erologonic}uintg1concrefinementThreads=8{prodent}}?{ergonomic}??size_t?InitialHeapSize???????????=?526385152??????{product}?{ergonomic}??size_tMarkStackSize?????????????=?4194304????????{product}?{ergonomic}??size_t?MaxHeapSize???????????????=?8403288064?????{product}?{ergonomic}??size_t?MaxNewSize????????????????=?5041553408?????{product}?{ergonomic}??size_t?MinHeapDeltaBytes?????????=?2097152????????{product}{ergonomic}uintxNonNMethodCodeHeapSize?????=?5836300????????{pd?product}?{ergonomic}??uintx?NonProfiledCodeHeapSize????=?122910970??????{pd?product}?{ergonomic}??uintx?ProfiledCodeHeapSize???????=?122910970??????{pd?product}?{ergonomic}??uintx?ReservedCodeCacheSize??????=?251658240??????{pd?product}?{ergonomic}??bool?SegmentedCodeCache??????????=?true???????????{product}{energolonic}boolusecompressedClassPointers=true{lp64_product}{ergononic}boolusecompressedoops=true{lp64_product}{ergologic}{ergolonic}booluseg1gc=true{true{true{product}{堆设置为32GB的1/64(大约512MB),最大堆设置为32GB的1/4(8GB)。在每个域中都具有高度可配置性。配置选项主要分为三种类型:标准:基本启动选项,例如-classpath在HotSpotJVM实现中很常见。-X:用于配置HotSpotJVM通用属性的非标准选项,例如控制最大堆大小(-Xmx);不能保证所有HotSpotJVM实现都支持这些。-XX:用于配置HotSpotJVM高级属性的高级选项。根据文档,这些内容如有更改,恕不另行通知,但Java团队有一个管理良好的流程来删除它们。六、-XX选项很多-XX选项可以进一步表征如下:Product。这些是最常用的-XX选项。实验性的。这些选项与HotSpotJVM中的实验性功能相关,可能尚未准备好用于生产。这些选项允许您试验新的HotSpotJVM功能,您需要通过指定来解锁它们:-XX:+UnlockExperimentalVMOptions例如,可以像这样启用JDK11中的ZGC垃圾收集器:java-XX:+UnlockExperimentalVMOptions-XX:+UseZGC一旦实验性功能准备好投入生产,控制它的选项就不再属于实验性,不需要解锁。ZGC收集器成为JDK15中的一个产品选项。易于管理。这些选项也可以在运行时通过MXBeanAPI或其他JDK工具进行设置。例如,要显示HotSpotJVM线程转储中java.util.concurrent类持有的锁,请使用:java-XX:+PrintConcurrentLocksDiagnostic。这些选项与访问有关HotSpotJVM的高级诊断信息有关。这些选项要求您使用以下选项来工作:-XX:+UnlockDiagnosticVMOptions一个示例诊断选项是:-XX:+LogCompilation它指示HotSpotJVM输出一个日志文件,其中包含JIT编译器所做的所有优化的详细信息。您可以检查此输出以查看程序的哪些部分已优化,并确定可能未按预期优化的程序部分。LogCompilation输出是冗长的,但可以在JITWatch等工具中可视化,它可以告诉您方法内联、转义分析、锁省略以及HotSpotJVM对您运行的代码所做的其他优化。发展。这些选项允许配置和调试最高级的HotSpotJVM设置,并且需要使用特殊的HotSpotJVM构建进行调试,然后才能访问它们。7.添加和删除选项选项开关的添加和删除是在HotSpotJVM中主要功能的出现或弃用之后进行的。这里有一些值得注意的点。在JDK9中,许多-XX:+Print...和-XX:+Trace...日志记录选项被删除并替换为-Xlog选项以控制JEP158引入的统一日志记录子系统。添加选项后实验性的ZGC、Epsilon和Shenandoah垃圾收集器,选项数量在JDK11中达到惊人的1504个峰值。随着并发标记清除(CMS)垃圾收集器的移除,数据量在JDK14中显着下降,如JEP363中所述.图4.OpenJDK每个版本中的选项总数(包括生产、实验、诊断和开发)表1.OpenJDK16之前的HotSpotJVM选项从OpenJDK17中删除表2.OpenJDK17中新添加的HotSpotJVM选项八,配置项的生命周期那么HotSpotJVM开发团队是如何管理选项的删除的呢?从JDK9开始,删除-XX选项的过程已扩展为三步过程:弃用、弃用和过期,以便向用户提供大量警告,告知他们的Java命令行可能很快需要更新。让我们看看HotSpotJVM是如何使用-XX:+AggressiveOpts选项的,这个选项在JDK11中被弃用,在JDK12中被弃用,最后在JDK13中过期。弃用选项。虽然支持这些选项,但会打印一条警告,让您知道将来可能会删除支持,例如:./jdk11/bin/java-XX:+AggressiveOptsOpenJDK64-BitServerVMwarning:OptionAggressiveOptswasdeprecatedin11.0版,可能会在未来的版本中删除。过时的选项。这些选项虽然已删除,但仍可在命令行上使用。(程序)将打印一条警告,让您知道这些选项将来可能不会被接受,例如:./jdk12/bin/java-XX:+AggressiveOptsOpenJDK64-BitServerVMwarning:IgnoringoptionAggressiveOpts;在12.0过期选项中删除了支持。这些是已弃用或过时的选项,其accept_until版本小于或等于当前JDK版本。当这些选项在其过时的JDK版本中使用时会打印警告,例如:./jdk13/bin/java-XX:+AggressiveOptsOpenJDK64-BitServerVMwarning:IgnoringoptionAggressiveOpts;在12.0中删除支持完全失败(不可用)。一旦你在旧版本的JDK中使用过时的配置,HotSpotJVM将在通过此选项并打印警告后启动失败,例如:./jdk14/bin/java-XX:+AggressiveOptsUnrecognizedVMoption'AggressiveOpts'Error:无法创建Java虚拟机。错误:发生致命异常。程序将会退出。不幸的是,并非所有期权都以这种有序的方式退出。例如,JDK9在引入统一日志记录和强大的-Xlog选项时放弃了对大量选项的支持,如NicolaiPalog的博客中所述。Java文档站点上也有一个页面,参考:ConvertGCLoggingFlagstoXlog。九。迁移到更高版本的JDK那么,您打算将Java启动脚本命令迁移到更高版本的JDK吗?也许您使用的是unity启动脚本,其中充满了您不熟悉的选项和配置,并且担心调整会影响应用程序的稳定性。您可以使用JaCoLine,Java命令行检查器来帮助您。粘贴命令,选择目标平台,然后分析您的配置选项将如何工作。参见图5。图5.使用JaCoLine分析命令行选项十、JVM参数配置建议虽然说到HotSpotJVM调优并没有放之四海而皆准的建议,但相信肯定有一些选项可以帮助您更好地理解执行程序并做出明智的配置选择。以下选项在JDK11及更高版本中可用。我之所以选择这些开关,是因为许多开发人员还没有迁移到更高版本的Java。请记住,这些是可选的;HotSpotJVM的默认设置非常好。首先,了解内存使用情况。在HotSpotJVM中分配内存很便宜。垃圾收集成本是指热点JVM清理堆中不再需要的对象时,以执行暂停的形式过期后的消耗。在提高应用程序性能和稳定性方面,了解代码进行的堆分配和由此产生的GC行为可能是最容易解决的问题,因为堆和GC配置之间的不匹配以及应用程序的分配行为会导致过度挂起,从而中断申请的过程。使用JaCoLineStatistics网页确认配置堆和GC日志记录是JaCoLine检查的所有命令行中最常用的选项。要配置堆,请考虑以下问题的答案:最大堆的正常预期内存使用量是多少?-Xmx设置最大堆大小,例如:-Xmx8g。-XX:MaxRAMPercentage=n将最大堆设置为总RAM的百分比。您期望堆多快达到其最大值?-Xms设置初始堆大小,例如:-Xms256m。-XX:InitialRAMPercentage=n将最大堆设置为总RAM的百分比。如果希望堆快速增长,可以将初始堆设置得更靠近最大堆。要处理OutOfMemory错误,您需要考虑当应用程序内存不足时HotSpotJVM应该如何表现。-XX:+ExitOnOutOfMemoryError告诉HotSpotJVM在出现第一个OutOfMemory错误时退出。如果HotSpotJVM将自动重启,这将很有用。-XX:+HeapDumpOnOutOfMemoryError通过将堆的内容转储到java_pid.hprof文件来帮助诊断内存泄漏。-XX:HeapDumpPath定义堆转储路径。其次,选择垃圾收集器。大多数硬件上的JDK11ergonomicsprocess会默认选择G1GC收集器,但它不是JDK11及更高版本中的唯一选项。其他可用的垃圾收集器有:-XX:+UseSerialGC选择串行收集器,它在单个线程上执行所有GC工作。-XX:+UseParallelGC选择并行(吞吐量)收集器,它可以使用多个线程来执行压缩。-XX:+UseConcMarkSweepGC选择CMS收集器。请注意,CMS收集器在JDK9中已弃用并在JDK14中删除。-XX:+UnlockExperimentalVMOptions-XX:+UseZGC选择ZGC收集器(在JDK11中为实验性,在JDK14及更高版本中为标准;因此您不需要此转变)。可以在HotSpot虚拟机垃圾收集调优指南中找到有关为您的应用程序选择收集器的建议。这是JDK11的文档版本;如果您使用的是更高版本的Java,请搜索更新的文档。为避免过早提升,请考虑您的应用程序是否以高分配率创建了短期对象。这可能会导致过早地将短寿命对象提升到老年代堆空间,它们将在那里累积,直到需要进行完整的垃圾回收。-XX:NewSize=n定义了新生代的初始大小。-XX:MaxNewSize=n定义了新一代的最大大小。-XX:MaxTenuringThreshold=n是一个对象在被提升到老年代之前可以存活的最大新生代收集次数。要记录内存使用情况和GC活动:使用-XX:+UnlockDiagnosticVMOptions?XX:NativeMemoryTracking=summary?XX:+PrintNMTStatistics获取HotSpotJVM退出时内存使用情况的完整详细信息。启用GC日志记录:-Xlog:gc提供基本的GC日志记录。-Xlog:gc*提供详细的GC日志记录。最后,了解JIT编译器如何优化您的代码。一旦您对应用程序的GC暂停处于可接受的水平感到满意,您就可以检查HotSpotJVM的JIT编译器是否正在优化您认为性能关键的程序部分。启用简单的编译日志记录如下:-XX:+PrintCompilation将有关每个JIT编译的基本信息打印到控制台。-XX:+UnlockDiagnosticVMOptions-XX:+PrintCompilation-XX:+PrintInlining添加有关方法内联的信息。示例输出:java-XX:+PrintCompilation7713java.lang.StringLatin1::hashCode(42字节)7823java.util.concurrent.ConcurrentHashMap::tabAt(22字节)783.internal.jdmik。Unsafe::getObjectAcquire(7字节)8043java.lang.Object::(1字节)8053java.lang.String::isLatin1(19字节)8063java.lang.String::hashCode(49bytes)输出中的项目(从左到右)如下:PrintCompilation在《Java JIT 编译器解释 – 第 1 部分》文章中有描述。将JIT信息记录到控制台对于检查方法是JIT编译的还是内联的(或两者)很有用,但如果您想更深入地了解JIT优化,则需要启用详细日志记录。使用-XX:+UnlockDiagnosticVMOptions?XX:+LogCompilation?XX:LogFile=jit.log启用详细编译日志记录。支持XML格式的详细编译日志记录,可以在JITWatch等工具中进行分析。您可以从BenEvans的“使用JITWatch了解JavaJIT编译,第1部分”以及第2部分和第3部分了解有关JITWatch的更多信息。
