写点什么

JVM C1、C2 编译器

作者:FunTester
  • 2023-12-04
    河北
  • 本文字数:3266 字

    阅读完需:约 11 分钟

Java 虚拟机创建了 C1 和 C2 编译器线程,用以优化应用程序的性能。但是有时这些线程会消耗大量 CPU 资源。在这篇文章中,我们将深入探讨 C1 和 C2 编译器线程,以及如何解决它们可能导致的高 CPU 消耗问题。

Hotspot JIT

HotSpot JIT(Just-In-Time)编译器是 Java 虚拟机(JVM)中的即时编译器,它负责将 Java 字节码转换为本地机器代码。HotSpot 是 Oracle JDK 和 OpenJDK 中默认的 JVM 实现,它包含两个主要的即时编译器:C1(Client Compiler)和 C2(Server Compiler)。


HotSpot JIT 编译器的工作方式是在运行时动态地将经常执行的字节码编译为本地机器代码,以提高程序的性能。它通过以下步骤实现:


  1. 解释执行(Interpretation):初始阶段,JVM 会对 Java 字节码进行解释执行,不生成本地机器代码,这使得程序可以立即运行。

  2. 即时编译(Just-In-Time Compilation):当某段代码(通常是被频繁执行的热点代码)被识别为性能瓶颈时,HotSpot JIT 编译器将这些热点代码编译成本地机器代码。这个过程包括对代码进行优化,以提高执行速度。

  3. 本地机器代码执行(Execution of Native Code):一旦代码被编译成本地机器代码,JVM 会直接执行这些代码,而不是再次解释执行对应的字节码。


C1 编译器通常用于快速启动和简单的应用程序,因为它生成的代码速度较快,但优化程度较低。而 C2 编译器更加激进,会花费更多时间进行更深层次的优化,生成更高效的本地机器代码,适用于需要更高性能的场景。


HotSpot JIT 编译器的工作方式有助于提高 Java 程序的性能,因为它能够在运行时优化热点代码,将其转换为更高效的本地机器代码,从而减少解释执行的开销,提高程序运行速度。

代码缓存

代码缓存(Code Cache)是 Java 虚拟机(JVM)中用于存储已编译代码的特定区域。在 JIT(Just-In-Time)编译器将 Java 字节码编译成本地机器代码时,这些生成的本地机器代码被存储在代码缓存中。


代码缓存的作用是保存已经编译过的代码,以便在程序的后续执行中直接使用这些本地机器代码,而无需重复地进行编译。这样可以提高程序的性能,因为避免了重复的编译过程,减少了解释执行的开销。

C1 和 C2 编译器区别

在 Java 早期阶段,存在两种类型的 JIT(即时编译)编译器,分别是 Client(客户端)和 Server(服务器)。根据所需的 JIT 编译器类型,需要下载并安装相应的 JDK。例如,如果您正在构建桌面应用程序,则需要下载具有“客户端”JIT 编译器的 JDK;如果是构建服务器应用程序,则需要下载具有“服务器”JIT 编译器的 JDK。


一旦应用程序启动,客户端 JIT 编译器就会开始对代码进行编译。而服务器 JIT 编译器则会观察代码执行相当长的一段时间。根据其获取的执行知识,服务器 JIT 编译器将开始进行 JIT 编译。尽管服务器 JIT 编译速度较慢,但生成的代码将比客户端 JIT 编译器生成的代码更优化,性能更出色。


然而,现代的 JDK 现在内置了客户端和服务器 JIT 编译器。这两个编译器都尝试对应用程序代码进行优化。在应用程序启动阶段,会使用客户端 JIT 编译器对代码进行编译。随着程序执行知识的积累,随后会采用服务器 JIT 编译器对代码进行编译。这种方法在 JVM 中被称为分层编译。


JDK 开发人员通常将这两种编译器称为客户端和服务器 JIT 编译器,而内部则分别称为 c1 和 c2 编译器。因此,客户端 JIT 编译器所使用的线程被称为 C1 编译器线程,而服务器 JIT 编译器所使用的线程被称为 C2 编译器线程。

C1、C2 编译器线程

C1、C2 编译器线程的默认数量根据运行应用程序的容器/设备上可用的 CPU 数量确定。下表总结了 C1、C2 编译器线程的默认数量:


C1、C2 编译器优化

当 c1 和 c2 编译器线程消耗大量 CPU 时,以下是解决该问题的潜在解决方案:

什么都不做

如果 C2 编译器线程的 CPU 消耗只是间歇性地偏高而不是持续性的,并且这种情况并未对您的应用程序性能造成明显影响,可以考虑暂时忽略该问题。在某些情况下,临时的 CPU 高消耗可能是正常的,可能是因为 JIT 编译器正在进行优化或在应用程序启动后初始编译所致。


然而,如果这种间歇性高 CPU 消耗开始对应用程序的性能产生负面影响,或者频繁发生,并且持续时间较长,那么可能需要进一步调查和解决。此时,可以考虑采取一些步骤,例如监视 JIT 编译器的行为、分析编译日志、调整 JVM 参数或升级到更新的 JVM 版本,以寻找潜在的解决方案。


总体来说,仅当间歇性的 C2 编译器线程高 CPU 消耗并未对应用程序的整体性能产生重大影响时,暂时忽略该问题可能是一个可行的做法。但如果情况变得更加频繁或持续,可能需要更深入地调查和处理。

分层编译

-XX:-TieredCompilation JVM 参数传递给应用程序将禁用 JIT(Just-In-Time)热点编译。这意味着代码将不会根据执行频率进行动态优化,从而可能降低 CPU 消耗。然而,需要注意的是,作为副作用,您的应用程序性能可能会受到影响,因为禁用了 JIT 编译会导致代码执行时不再进行实时优化。


此参数的使用是一种权衡:通过降低 CPU 消耗来解决高 CPU 消耗问题,但可能以牺牲应用程序性能为代价。因此,在使用这个参数之前,需要仔细权衡,并在实际应用程序环境中进行测试,以确保最终结果不会对应用程序的整体性能产生不可接受的影响。

设置分层等级

当 CPU 峰值是由 C2 编译器线程单独引起时,你可以选择单独关闭 C2 编译。通过传递 -XX:TieredStopAtLevel=3 参数,可以实现这一目的。此参数的作用是仅启用 C1 编译器,同时禁用 C2 编译器。


这种方法可以降低 CPU 消耗,因为禁用 C2 编译器会使系统只使用较轻量级的 C1 编译器,但需要注意的是,这可能会影响到应用程序的性能。


在使用此参数之前,建议进行详尽测试,以确保对应用程序性能的影响在可接受范围内。选择禁用 C2 编译器应慎重考虑,因为可能会牺牲应用程序的性能优化能力。


编译分为四层:


打印编译信息

-XX:+PrintCompilation 是一个非常有用的 JVM 参数。通过传递此参数给您的应用程序,JVM 将会打印有关应用程序编译过程的详细信息,这可以帮助你更好地了解代码的实际编译情况。


打印编译信息可以提供有关哪些方法被编译、何时被编译以及使用了哪种类型的编译器(比如 C1 或 C2)等方面的详细信息。这对于调整和优化应用程序的性能非常有帮助,因为您可以通过查看输出信息来了解编译器在何处花费时间,从而有针对性地进行优化。


但需要注意的是,输出的信息可能会非常详细和庞大,可能会对系统性能产生一定的影响。因此,在生产环境中使用此参数时要小心谨慎,并且最好在测试环境中进行尝试和调整,以避免对实际应用程序的性能产生不必要的负面影响。

设置缓存区大小

Hotspot JIT 编译器在 JVM 内存中有一个代码缓存区域,用于存储它编译和优化的代码。默认情况下,代码缓存区域的大小为 240MB。


可以通过将 -XX:ReservedCodeCacheSize=N 传递给程序来增加代码缓存的大小。例如 -XX:ReservedCodeCacheSize=512m 进行指定。


增加代码缓存的大小有助于提高 JIT 编译器在其中存储优化代码的容量,从而有可能减少编译器线程的 CPU 消耗。这种调整可以为编译器提供更多的空间,以存储更多的编译代码,减少由于不断重编译代码而导致的性能损失。


增加代码缓存的大小也会占用更多的内存资源。在进行此类调整时,请确保考虑到系统的内存限制以及其他应用程序或组件对内存的需求,以避免因为过度分配内存而导致系统性能问题。最佳做法是进行适度的调整并在测试环境中进行验证,以确保对应用程序性能的提升并没有不必要地牺牲其他方面的系统资源。

设置编译线程数

可以使用参数 -XX:CICompilerCount 来控制 C2 编译器线程的数量。默认情况下,C2 编译器线程的数量由 JVM 根据 CPU 核心数量和其他因素自动确定。但有时可能会发现 C2 编译器线程数量较少,尤其是在具有多个 CPU 处理器或内核的系统上。


通过捕获线程转储并上传到适当的工具(如诊断工具或性能监控工具),我们可以查看 C2 编译器线程的实际数量。如果 C2 编译器线程数过少,您可以尝试使用 -XX:CICompilerCount=8 这样的参数来手动增加 C2 编译器线程的数量。


增加 C2 编译器线程的数量可能有助于提高 JIT 编译的并发性能,特别是在具有更多 CPU 核心的系统上。但请注意,过多的编译器线程可能会导致资源竞争和性能下降。因此,在调整此参数之前,请务必进行仔细的测试和评估,以确保其对应用程序性能的实际影响是积极的,并且不会造成其他系统方面的负面影响。


发布于: 刚刚阅读数: 3
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
JVM C1、C2编译器_FunTester_InfoQ写作社区