为什么 JVM 需要预热?

2022-08-31 20:14:51

我知道在Java虚拟机(JVM)中,预热是可能需要的,因为Java使用延迟加载过程加载类,因此您希望确保在启动主事务之前初始化对象。我是一C++开发人员,不必处理类似的要求。

但是,我无法理解的部分如下:

  1. 您应该预热代码的哪些部分?
  2. 即使我预热了代码的某些部分,它也会保持热状态多长时间(假设这个术语仅表示您的类对象在内存中保留多长时间)?
  3. 如果我有每次收到事件时都需要创建的对象,这会有什么帮助?

例如,假设应用程序预期通过套接字接收消息,并且事务可以是“新订单”、“修改订单”和“取消订单”或事务已确认。

请注意,该应用程序是关于高频交易(HFT)的,因此性能非常重要。


答案 1

您应该预热代码的哪些部分?

通常,您不必执行任何操作。但是,对于低延迟应用程序,应预热系统中的关键路径。你应该有单元测试,所以我建议你在启动时运行这些测试来预热代码。

即使您的代码已预热,您也必须确保 CPU 缓存也保持温暖。在阻塞操作(例如网络 IO)之后,您可以看到性能显着下降,长达 50 微秒。通常这不是问题,但如果你试图在大多数时间保持在50微秒以下,这在大多数时候都是一个问题。

注意:预热可以允许逃逸分析启动并将某些对象放在堆栈上。这意味着这些对象不需要优化。最好在优化代码之前对应用程序进行内存分析。

即使我预热了代码的某些部分,它也会保持热状态多长时间(假设这个术语仅表示您的类对象在内存中保留多长时间)?

没有时间限制。这取决于 JIt 是否检测它在优化代码时所做的假设是否被证明是错误的。

如果我有每次收到事件时都需要创建的对象,这会有什么帮助?

如果想要低延迟或高性能,则应创建尽可能少的对象。我的目标是产生少于 300 KB/秒的速率。有了这个分配率,你可以有一个足够大的伊甸园空间,每天小收集一次。

例如,假设应用程序预期通过套接字接收消息,并且事务可以是“新订单”、“修改订单”和“取消订单”或事务已确认。

我建议您尽可能多地重用对象,尽管如果它低于您的分配预算,则可能不值得担心。

请注意,该应用程序是关于高频交易(HFT)的,因此性能非常重要。

您可能对我们的开源软件感兴趣,该软件用于不同投资银行和对冲基金的高频交易系统。

http://chronicle.software/

我的生产应用程序用于高频交易,每一点延迟都可能是一个问题。很明显,在启动时,如果您不预热应用程序,它将导致几毫的高延迟。

特别是,您可能对 https://github.com/OpenHFT/Java-Thread-Affinity 感兴趣,因为此库可以帮助减少关键线程中的调度抖动。

还有人说,需要预热的代码的关键部分应该至少运行12K次(使用假消息),以便以优化的方式工作。它为什么以及如何工作?

代码是使用后台线程编译的。这意味着,即使一个方法可能有资格编译为本机代码,这并不意味着它已经这样做了,尤其是在编译器已经很忙的时候启动的时候。12K不是不合理的,但它可能会更高。


答案 2

变暖是指让一段代码运行足够多的次数,使JVM停止解释并编译为本机(至少是第一次)。一般来说,这是你不想做的事情。原因是JVM收集有关它在代码生成期间使用的相关代码的统计信息(类似于按配置文件优化)。因此,如果有问题的代码块被假数据“加热”,而假数据与真实数据具有不同的属性,则很可能会损害性能。

编辑:由于JVM无法执行整个程序静态分析(它无法知道应用程序将加载哪些代码),因此它可以从收集的统计信息中对类型进行一些猜测。例如,当在精确的调用位置调用虚函数(C++说话)并确定所有类型都具有相同的实现时,则调用被提升为直接调用(甚至内联)。如果后来这个假设被证明是错误的,那么旧代码必须“未编译”才能正常运行。AFAIK HotSpot将呼叫站点分类为单态(单一实现),双态(正好是两个。转换为 if (imp1-type) {imp1} else {imp2} ) 和完全多态性。.虚拟调度。

还有另一种发生重新编译的情况。当您有分层编译时。第一层将花费更少的时间尝试生成好的代码,如果方法“足够热”,那么更昂贵的编译时代码生成器就会启动。