HotSpot JVM Java 11 到 17 最佳配置项和开关
nanshan 2024-11-10 10:10 15 浏览 0 评论
一、前言
在本文中,你将了解 OpenJDK HotSpot Java 虚拟机 (HotSpot JVM) 中的一些系统知识,以及如何调整它们以获得最佳状态适应你的程序和运行环境。
HotSpot JVM 是一项了不起且灵活的技术。 它作为二进制版本适用于每个主要操作系统和 CPU 架构,从微型 Raspberry Pi Zero 一直到包含数百个 CPU 内核和 TB 级 RAM 的“大型”服务器。 由于 OpenJDK 是一个开源项目,HotSpot JVM 几乎可以针对任何其他系统进行编译,并且可以使用选项、开关和标志对其进行微调。
首先,这里有一些背景。 HotSpot JVM 的语言是字节码。 在撰写本文时,有超过 30 种编程语言可以编译成 HotSpot JVM 兼容的字节码,但迄今为止最受欢迎的、在全球拥有超过 800 万开发人员的当然是 Java。
Java 源代码被编译成字节码(如图 1 所示),以类文件的形式,使用 javac 编译器。 在现代开发中,这可能会被 Maven、Gradle 或基于 IDE 的编译器等构建工具抽象掉。
程序的字节码表示由 HotSpot JVM 在一个虚拟堆栈机上执行,该虚拟堆栈机知道多达256条不同的指令,每条指令由一个8位数字操作码标识;因此,名称是“字节码”。
字节码程序由解释器执行,该解释器获取每条指令,将其操作数压入堆栈,然后执行该指令,移除操作数并将结果留在堆栈中,如图 2 所示。
将程序执行从底层环境中抽象出来,赋予了 Java “一次编写,随处运行”的可移植性优势。 在一种架构上编译的类文件可以运行在完全不同架构的 HotSpot JVM 上执行。
如果你认为这种对底层硬件的抽象是以牺牲性能为代价的,那么你是对的。 这通常就是 HotSpot JVM 开关、选项和标志的用武之地。
二、JIT 即时编译
用可移植、功能丰富的高级语言(如Java)编写的程序如何挑战那些从“低级”、“不太友好”的编程语言(如C)编译为特定于体系结构的本机代码的程序的性能呢?
答案是 HotSpot JVM 包含了性能提升的即时(JIT)编译技术,它可以分析程序的执行情况,并有选择地优化它认为最有好处的部分。这些被称为程序的热点(因此,将其命名为 HotSpot JVM),它通过使用底层系统架构的知识动态地将它们编译成本地代码来实现这一点。
HotSpot JVM包含两个 JIT 编译器,称为 C1(客户端编译器)和 C2(服务器编译器),它们提供了不同的优化权衡。
- C1 提供了快速、简单的优化。
- C2 提供了需要更多分析的高级优化,而且应用成本更高。
自 JDK 8 发布以来,默认行为一直是在称为分层编译的模式下同时使用这两个编译器,其中 C1 提供了快速的速度提升,而 C2 在进行高级优化之前收集了足够的评测信息。生成的本机代码存储在热点 JVM 的内存区域中,称为代码缓存,如图3所示。
三、GC 垃圾回收
除了 JIT 技术之外,HotSpot JVM 还包括提高生产力和性能的功能,例如:多线程和自动内存管理以及垃圾收集 (GC) 策略的选择。
对象被分配在 HotSpot JVM 的一个称为堆的内存区域中,一旦这些对象不再被引用,垃圾收集器就可以将它们清理干净,并将它们使用的内存回收。
四、符合人体工程学的 HotSpot JVM
HotSpot JVM 具有如此多的灵活性和动态行为,你可能会担心如何配置它以最好地满足你的程序要求。 幸运的是,对于很多用例,你不需要进行任何手动调整。 HotSpot JVM 包含一个称为 ergonomic(人体工程学)的过程,它在启动时检查执行环境,并根据 CPU 内核数量和可用 RAM 数量为 GC 策略、堆大小和 JIT 编译器选择一些合理的默认值。 当前的默认值是:
- 垃圾收集器:G1 GC
- 初始堆:物理内存的 1/64
- 最大堆:物理内存的 1/4
- JIT 编译器:同时使用 C1 和 C2 的分层编译
通过使用选项 -XX:+PrintFlagsFinal 并使用 grep 命令搜索 ergonomic,你可以看到 HotSpot JVM 将为你的环境选择的所有 ergonomic 默认值,如下所示:
java -XX:+PrintFlagsFinal | grep ergonomic
intx CICompilerCount = 4 {product} {ergonomic}
uint ConcGCThreads = 2 {product} {ergonomic}
uint G1ConcRefinementThreads = 8 {product} {ergonomic}
size_t G1HeapRegionSize = 2097152 {product} {ergonomic}
uintx GCDrainStackTargetSize = 64 {product} {ergonomic}
size_t InitialHeapSize = 526385152 {product} {ergonomic}
size_t MarkStackSize = 4194304 {product} {ergonomic}
size_t MaxHeapSize = 8403288064 {product} {ergonomic}
size_t MaxNewSize = 5041553408 {product} {ergonomic}
size_t MinHeapDeltaBytes = 2097152 {product} {ergonomic}
uintx NonNMethodCodeHeapSize = 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} {ergonomic}
bool UseCompressedClassPointers = true {lp64_product} {ergonomic}
bool UseCompressedOops = true {lp64_product} {ergonomic}
bool UseG1GC = true {product} {ergonomic}
上面的输出来自具有 32 GB RAM 的机器上的 JDK 11,因此初始堆设置为 32 GB 的 1/64(约 512 MB),最大堆设置为 32 GB 的 1/4(8 GB)。
五、自定义
如果你认为默认的设置不适合你的应用程序,很高兴 HotSpot JVM 在每个领域都具有高度可配置性。
有三种主要类型的配置选项:
- 标准: 基本启动选项,例如 -classpath 在 HotSpot JVM 实现中很常见。
- -X: 用于配置 HotSpot JVM 的通用属性的非标准选项,例如控制最大堆大小 (-Xmx); 不能保证所有 HotSpot JVM 实现都支持这些。
- -XX: 用于配置 HotSpot JVM 的高级属性的高级选项。 根据文档,这些内容如有更改,恕不另行通知,但 Java 团队有一个管理良好的流程来删除它们。
六、-XX 选项
许多 -XX 选项可以进一步表征如下:
Product. 这些是最常用的 -XX 选项。
Experimental. 这些是与 HotSpot JVM 中的实验性功能相关的选项,这些功能可能尚未准备好投入生产。 这些选项允许你尝试新的 HotSpot JVM 功能,并且需要通过指定以下内容来解锁它们:
-XX:+UnlockExperimentalVMOptions
例如,在 JDK 11 中使用 ZGC 垃圾收集器可以这样开启:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
一旦一个实验性功能准备好投入生产,控制它的选项就不再被归类为实验性的,不需要解锁。 ZGC 收集器成为 JDK 15 中的 Product 选项。
Manageable. 这些选项也可以在运行时通过 MXBean API 或其他 JDK 工具设置。 例如,要在 HotSpot JVM 线程转储中显示 java.util.concurrent 类持有的锁,请使用:
java -XX:+PrintConcurrentLocks
Diagnostic. 这些选项与访问有关 HotSpot JVM 的高级诊断信息有关。 这些选项需要你使用以下内容才能使用:
-XX:+UnlockDiagnosticVMOptions
一个示例诊断选项是:
-XX:+LogCompilation
它指示 HotSpot JVM 输出一个日志文件,其中包含 JIT 编译器所做的所有优化的详细信息。 你可以检查此输出以了解程序的哪些部分已优化,并确定程序中可能未按预期优化的部分。LogCompilation 输出很详细,但可以在 JITWatch 等工具中可视化,它可以告诉你有关方法内联、逃逸分析、锁省略和 HotSpot JVM 对你运行的代码所做的其他优化。
Developmental. 这些选项允许配置和调试最高级的 HotSpot JVM 设置,并且在你访问它们之前需要使用特殊的 HotSpot JVM 构建调试。
七、添加和删除的选项
选项开关的添加和删除是在 HotSpot JVM 中主要功能的到来或弃用之后进行的。 这里有一些值得注意的地方。
- 在 JDK 9 中,许多 -XX:+Print... 和 -XX:+Trace... 日志选项被删除并替换为 -Xlog 选项,用于控制由 JEP 158 引入的统一日志记录子系统。
- 在添加了实验性 ZGC、Epsilon 和 Shenandoah 垃圾收集器的选项后,JDK11 中的选项数达到峰值,达到了惊人的 1504 个。
- 随着并发标记扫描(CMS)垃圾收集器的删除,JDK14 中的数据量大幅下降,如 JEP 363 中所述。
表 1. 从 OpenJDK 17 中删除的 OpenJDK 16 之前的 HotSpot JVM 选项
表 2. OpenJDK 17 新加入的 HotSpot JVM 选项
八、配置项的生命周期
那么 HotSpot JVM 开发团队如何管理选项的删除呢?自 JDK 9 以来,删除 -XX 选项的过程被扩展为三步过程:弃用、过时和过期,以向用户发出大量警告,提示他们的 Java命令行可能很快需要更新。
让我们看看 HotSpot JVM 如何对 -XX:+AggressiveOpts 选项作出的操作,该选项在 JDK 11 中被弃用,在 JDK 12 中被淘汰,最后在 JDK 13 中过期。
不推荐使用的选项。虽然可以支持这些选项,但会打印一条警告并让你知道将来可能会删除支持,例如:
./jdk11/bin/java -XX:+AggressiveOpts
OpenJDK 64-Bit Server VM warning: Option AggressiveOpts was deprecated in version 11.0 and will likely be removed in a future release.
过时的选项。这些选项虽然已被删除,但在命令行上仍被接受。(程序)会打印一条警告,让你知道这些选项将来可能不会被接受,例如:
./jdk12/bin/java -XX:+AggressiveOpts
OpenJDK 64-Bit Server VM warning: Ignoring option AggressiveOpts; support was removed in 12.0
过期的选项。 这些是不推荐使用或过时的选项,其 accept_until版本小于或等于当前JDK 版本。当这些选项在其过期的 JDK 版本中使用时,会打印一条警告,例如:
./jdk13/bin/java -XX:+AggressiveOpts
OpenJDK 64-Bit Server VM warning: Ignoring option AggressiveOpts; support was removed in 12.0
完全失败(不可用)。 当你一旦使用了某个老版本 JDK 中过时的配置时,HotSpot JVM 将在通过该选项并打印警告后启动失败,例如:
./jdk14/bin/java -XX:+AggressiveOpts
Unrecognized VM option 'AggressiveOpts'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
令人遗憾的是,并不是所有的 option 都以这种有序的方式退场。例如,JDK 9 在引入统一日志记录和强大的 -Xlog 选项时放弃了对大量选项的支持,这在 Nicolai Palog 的博客中有详细介绍。Java 文档网站上还有一个页面,参考:Convert GC Logging Flags to Xlog。
九、迁移到高版本的 JDK
那么,你是否准备将 Java 启动脚本命令迁移到更高版本的 JDK? 也许你使用了统一启动脚本,其中充满了你不熟悉的选项和配置,并且担心调整会影响应用程序的稳定性。
你可以使用 JaCoLine,Java 命令行检查器来帮助你。 粘贴命令,选择目标平台,然后分析你的配置选项将如何工作。 见图 5。
十、JVM 参数配置建议
虽然在 HotSpot JVM 调优方面没有一刀切的建议,但我相信肯定有一些选项可以帮助你更好地了解程序的执行并做出明智的配置选择。
以下选项在 JDK 11 及更高版本中可用。 我选择这些开关是因为许多开发人员还没有转向更高版本的 Java。 请记住,这些都是可选的; HotSpot JVM 的默认设置非常好。
首先,了解内存使用情况。 在 HotSpot JVM 中分配内存很便宜。 垃圾收集成本是指当热点JVM清理堆中不再需要的对象时,稍后以执行暂停的形式到期的消耗。
在提高应用程序性能和稳定性方面,了解代码进行的堆分配以及由此产生的GC行为可能是最容易解决的问题,因为堆和GC配置以及应用程序的分配行为之间的不匹配会导致过度暂停,从而中断应用程序的进程。
使用 JaCoLine Statistics 网页确认配置堆和 GC 日志记录是 JaCoLine 检查的所有命令行中最受欢迎的选项。
要配置堆,请考虑以下问题的答案:
- 正常情况下预期最大堆的内存使用量是多少? -Xmx 设置最大堆大小,例如:-Xmx8g。-XX:MaxRAMPercentage=n 将最大堆设置为总 RAM 的百分比。
- 你期望堆多快达到其最大值? -Xms 设置初始堆大小,例如:-Xms256m.-XX:InitialRAMPercentage=n 将最大堆设置为总 RAM 的百分比。如果希望堆快速增长,可以将初始堆设置为更接近最大堆。
要处理 OutOfMemory 错误,需要考虑在应用程序内存不足时 HotSpot JVM 应该如何工作。
- -XX:+ExitOnOutOfMemoryError 告诉 HotSpot JVM 在出现第一个 OutOfMemory 错误时退出。 如果 HotSpot JVM 将自动重新启动,这会很有用。
- -XX:+HeapDumpOnOutOfMemoryError 通过将堆的内容转储到 java_pid.hprof 文件来帮助诊断内存泄漏。
- -XX:HeapDumpPath 定义 heap dump 路径。
其次,选择垃圾收集器。 大多数硬件上的 JDK 11 人体工程学过程将默认选择 G1GC 收集器,但它不是 JDK 11 及更高版本中的唯一选择。
其他可用的垃圾收集器是:
- -XX:+UseSerialGC 选择串行收集器,它在单个线程上执行所有 GC 工作。
- -XX:+UseParallelGC 选择并行(吞吐量)收集器,它可以使用多个线程执行压缩。
- -XX:+UseConcMarkSweepGC 选择 CMS 收集器。 请注意,CMS 收集器在 JDK 9 中已被弃用,并在 JDK 14 中被删除。
- -XX:+UnlockExperimentalVMOptions -XX:+UseZGC 选择 ZGC 收集器(在 JDK 11 中是实验性的,在 JDK 14 及更高版本中是标准功能;因此你不需要此开关)。
可以在 HotSpot Virtual Machine Garbage Collection Tuning Guide 中找到有关为你的应用程序选择收集器的建议。 这是 JDK 11 的文档版本; 如果你使用的是更高版本的 Java,请搜索更新的文档。
为避免过早提升,请考虑你的应用程序是否以高分配率创建短期对象。 这可能导致短期对象过早提升到老年代堆空间,在那里它们将累积,直到需要完整的垃圾收集。
- -XX:NewSize=n 定义新生代的初始大小。
- -XX:MaxNewSize=n 定义新生代的最大大小。
- -XX:MaxTenuringThreshold=n 是一个对象在提升到老年代之前可以存活的最大新生代集合数。
要记录内存使用情况和 GC 活动,请执行以下操作:
- 使用-XX:+UnlockDiagnosticVMOptions ?XX:NativeMemoryTracking=summary ?XX:+PrintNMTStatistics获取 HotSpot JVM 退出时内存使用情况的完整细节。
- 使用以下命令启用 GC 日志记录: -Xlog:gc 提供基本的 GC 日志记录。-Xlog:gc* 提供详细的 GC 日志记录。
最后,了解 JIT 编译器如何优化你的代码。 一旦你对应用程序的 GC 停顿处于可接受的水平感到满意,你就可以检查 HotSpot JVM 的 JIT 编译器是否正在优化你认为对性能很重要的程序部分。
启用简单的编译日志,如下所示:
- -XX:+PrintCompilation 将有关每个 JIT 编译的基本信息打印到控制台。
- -XX:+UnlockDiagnosticVMOptions ?XX:+PrintCompilation ?XX:+PrintInlining 添加有关方法内联的信息。
输出示例:
java -XX:+PrintCompilation
77 1 3 java.lang.StringLatin1::hashCode (42 bytes)
78 2 3 java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)
78 3 3 jdk.internal.misc.Unsafe::getObjectAcquire (7 bytes)
80 4 3 java.lang.Object:: (1 bytes)
80 5 3 java.lang.String::isLatin1 (19 bytes)
80 6 3 java.lang.String::hashCode (49 bytes)
输出中的项目(从左到右)如下:
PrintCompilation在《Java JIT 编译器解释 – 第 1 部分》文章中有说明。
将 JIT 信息记录到控制台对于检查方法是被 JIT 编译还是内联(或两者)非常有用,但如果你想更深入地了解 JIT 优化,则需要启用详细的日志记录。
使用 -XX:+UnlockDiagnosticVMOptions ?XX:+LogCompilation ?XX:LogFile=jit.log 启用详细的编译日志记录。 它支持详细的 XML 格式编译日志记录,可以在 JITWatch 等工具中进行分析。 你可以从 Ben Evans 的“使用 JITWatch 理解 Java JIT 编译,第 1 部分”以及第 2 部分和第 3 部分中了解有关 JITWatch 的更多信息。
译者说:
大家好,我是 如梦技术春哥(mica、mica-mqtt 开源作者)感谢一起参与翻译和校对的张亚东(JustAuth、Jap 开源作者)、xkcoding(spring-boot-demo 开源作者)同学,关注我们更多优秀开源助你早日走上人生巅峰!!!
相关推荐
- F5负载均衡器如何通过irules实现应用的灵活转发?
-
F5是非常强大的商业负载均衡器。除了处理性能强劲,以及高稳定性之外,F5还可以通过irules编写强大灵活的转发规则,实现web业务的灵活应用。irules是基于TCL语法的,每个iRules必须包含...
- 映射域名到NAS
-
前面介绍已经将域名映射到家庭路由器上,现在只需要在路由器上设置一下端口转发即可。假设NAS在内网的IP是192.168.1.100,NAS管理端口2000.你的域名是www.xxx.com,配置外部端...
- 转发(Forward)和重定向(Redirect)的区别
-
转发是服务器行为,重定向是客户端行为。转发(Forward)通过RequestDispatcher对象的forward(HttpServletRequestrequest,HttpServletRe...
- SpringBoot应用中使用拦截器实现路由转发
-
1、背景项目中有一个SpringBoot开发的微服务,经过业务多年的演进,代码已经累积到令人恐怖的规模,亟需重构,将之拆解成多个微服务。该微服务的接口庞大,调用关系非常复杂,且实施重构的人员大部分不是...
- 公司想搭建个网站,网站如何进行域名解析?
-
域名解析是将域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转...
- 域名和IP地址什么关系?如何通过域名解析IP?
-
一般情况下,访客通过域名和IP地址都能访问到网站,那么两者之间有什么关系吗?本文中科三方针对域名和IP地址的关系和区别,以及如何实现域名与IP的绑定做下介绍。域名与IP地址之间的关系IP地址是计算机的...
- 分享网站域名301重定向的知识
-
网站域名做301重定向操作时,一般需要由专业的技术来协助完成,如果用户自己在维护,可以按照相应的说明进行操作。好了,下面说说重点,域名301重定向的操作步骤。首先,根据HTTP协议,在客户端向服务器发...
- NAS外网到底安全吗?一文看懂HTTP/HTTPS和SSL证书
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:可爱的小cherry搭好了NAS,但是不懂做好网络加密,那么隐私泄露也会随时发生!大家好,这里是Cherry,喜爱折腾、玩数码,热衷于分享数...
- ForwardEmail免费、开源、加密的邮件转发服务
-
ForwardEmail是一款免费、加密和开源的邮件转发服务,设置简单只需4步即可正常使用,通过测试来看也要比ImprovMX好得多,转发近乎秒到且未进入垃圾箱(仅以Mailbox.org发送、Out...
- 使用CloudFlare进行域名重定向
-
当网站变更域名的时候,经常会使用域名重定向的方式,将老域名指向到新域名,这通常叫做:URL转发(URLFORWARDING),善于使用URL转发,对SEO来说非常有用,因为用这种方式能明确告知搜索引...
- 要将端口5002和5003通过Nginx代理到一个域名上的操作笔记
-
要将端口5002和5003通过Nginx代理到域名www.4rvi.cn的不同路径下,请按照以下步骤配置Nginx:步骤说明创建或编辑Nginx配置文件通常配置文件位于/etc/nginx/sites...
- SEO浅谈:网站域名重定向的三种方式
-
在大多数情况下,我们输入网站访问网站的时候,很难发现www.***.com和***.com的区别,因为一般的网站主,都会把这两个域名指向到同一网站。但是对于网站运营和优化来说,www.***.com和...
- 花生壳出现诊断域名与转发服务器ip不一致的解决办法
-
出现诊断域名与转发服务器ip不一致您可以:1、更改客户端所处主机的drs为223.5.5.5备用dns为119.29.29.29;2、在windows上进入命令提示符输入ipconfig/flush...
- 涨知识了!带你认识什么是域名
-
1、什么是域名从技术角度来看,域名是在Internet上解决IP地址对应的一种方法。一个完整的域名由两个或两个以上部分组成,各部分之间用英文的句号“.”来分隔。如“abc.com”。其中“com”称...
- 域名被跳转到其他网站是怎么回事
-
当你输入域名时被跳转到另一个网站,这可能是由几种原因造成的:一、域名可能配置了域名转发服务。无论何时有人访问域名,比如.com、.top等,都会自动重定向到另一个指定的URL,这通常是在域名注册商设...
你 发表评论:
欢迎- 一周热门
-
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
[常用工具] OpenCV_contrib库在windows下编译使用指南
-
WindowsServer2022|配置NTP服务器的命令
-
Ubuntu系统Daphne + Nginx + supervisor部署Django项目
-
WIN11 安装配置 linux 子系统 Ubuntu 图形界面 桌面系统
-
解决Linux终端中“-bash: nano: command not found”问题
-
NBA 2K25虚拟内存不足/爆内存/内存占用100% 一文速解
-
Linux 中的文件描述符是什么?(linux 打开文件表 文件描述符)
-
K3s禁用Service Load Balancer,解决获取浏览器IP不正确问题
-
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)