java 为什么JVM需要预热?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36198278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 01:03:22  来源:igfitidea点击:

Why does the JVM require warmup?

javagarbage-collectionjvmlow-latencyhft

提问by Suparna

I understand that in the Java virtual machine (JVM), warmup is potentially required as Java loads classes using a lazy loading process and as such you want to ensure that the objects are initialized before you start the main transactions. I am a C++ developer and have not had to deal with similar requirements.

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

However, the parts I am not able to understand are the following:

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

  1. Which parts of the code should you warm up?
  2. Even if I warm up some parts of the code, how long does it remain warm (assuming this term only means how long your class objects remain in-memory)?
  3. How does it help if I have objects which need to be created each time I receive an event?
  1. 你应该预热代码的哪些部分?
  2. 即使我对代码的某些部分进行了预热,它还能保持多长时间(假设这个术语仅表示您的类对象在内存中保持多长时间)?
  3. 如果每次收到事件时都需要创建对象,这有什么帮助?

Consider for an example an application that is expected to receive messages over a socket and the transactions could be New Order, Modify Order and Cancel Order or transaction confirmed.

考虑一个例子,一个应用程序希望通过套接字接收消息,交易可以是新订单、修改订单和取消订单或确认交易。

Note that the application is about High Frequency Trading (HFT) so performance is of extreme importance.

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

采纳答案by Peter Lawrey

Which parts of the code should you warm up?

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

Usually, you don't have to do anything. However for a low latency application, you should warmup the critical path in your system. You should have unit tests, so I suggest you run those on start up to warmup up the code.

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

Even once your code is warmed up, you have to ensure your CPU caches stay warm as well. You can see a significant slow down in performance after a blocking operation e.g. network IO, for up to 50 micro-seconds. Usually this is not a problem but if you are trying to stay under say 50 micro-seconds most of the time, this will be a problem most of the time.

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

Note: Warmup can allow Escape Analysis to kick in and place some objects on the stack. This means such objects don't need to be optimised away. It is better to memory profile your application before optimising your code.

注意:Warmup 可以让 Escape Analysis 启动并在堆栈中放置一些对象。这意味着不需要优化这些对象。最好在优化代码之前对应用程序进行内存分析。

Even if I warm up some parts of the code, how long does it remain warm (assuming this term only means how long your class objects remain in-memory)?

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

There is no time limit. It depends on whether the JIt detects whether the assumption it made when optimising the code turned out to be incorrect.

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

How does it help if I have objects which need to be created each time I receive an event?

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

If you want low latency, or high performance, you should create as little objects as possible. I aim to produce less than 300 KB/sec. With this allocation rate you can have an Eden space large enough to minor collect once a day.

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

Consider for an example an application that is expected to receive messages over a socket and the transactions could be New Order, Modify Order and Cancel Order or transaction confirmed.

考虑一个例子,一个应用程序希望通过套接字接收消息,交易可以是新订单、修改订单和取消订单或确认交易。

I suggest you re-use objects as much as possible, though if it's under your allocation budget, it may not be worth worrying about.

我建议您尽可能多地重复使用对象,但如果它在您的分配预算内,则可能不值得担心。

Note that the application is about High Frequency Trading (HFT) so performance is of extreme importance.

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

You might be interested in our open source software which is used for HFT systems at different Investment Banks and Hedge Funds.

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

http://chronicle.software/

http://chronicle.software/

My production application is used for High frequency trading and every bit of latency can be an issue. It is kind of clear that at startup if you don't warmup your application, it will lead to high latency of few millis.

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

In particular you might be interested in https://github.com/OpenHFT/Java-Thread-Affinityas this library can help reduce scheduling jitter in your critical threads.

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

And also it is said that the critical sections of code which requires warmup should be ran (with fake messages) atleast 12K times for it to work in an optimized manner. Why and how does it work?

而且据说需要预热的代码的关键部分应该运行(使用假消息)至少 12K 次才能以优化的方式工作。为什么以及如何运作?

Code is compiled using background thread(s). This means that even though a method might be eligible for compiling to native code, it doesn't mean that it has done so esp on startup when the compiler is pretty busy already. 12K is not unreasonable, but it could be higher.

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

回答by MB Reynolds

Warming refers to having a piece of code run enough times that the JVM stops interpreting and compiles to native (at least for the first time). Generally that's something you don't want to do. The reason is that the JVM gathers statistics about the code in question that it uses during code generation (akin to profile guided optimizations). So if a code chunk in question is "warmed" with fake data which has different properties than the real data you could well be hurting performance.

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

EDIT: Since the JVM cannot perform whole-program static analysis (it can't know what code is going to be loaded by the application) it can instead make some guesses about types from the statistics it has gathered. As an example when calling a virtual function (in C++ speak) at an exact calling location and it determines that all types have the same implementation, then the call is promoted to direct call (or even inlined). If later that assumption if proven to be wrong, then the old code must be "uncompiled" to behave properly. AFAIK HotSpot classifies call-sites as monomorphic (single implementation), bi-morphic (exactly two..transformed into if (imp1-type) {imp1} else {imp2} ) and full polymorphic..virtual dispatch.

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

And there's another case in which recompiling occurs..when you have tiered-compilation. The first tier will spend less time trying to produce good code and if the method is "hot-enough" then the more expensive compile-time code generator kicks in.

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

回答by Kayaman

Warm-up is rarely required. It's relevant when doing for example performance tests, to make sure that the JIT warm-up time doesn't skew the results.

很少需要热身。例如,在进行性能测试时,这很重要,以确保 JIT 预热时间不会扭曲结果。

In normal production code you rarely see code that's meant for warm-up. The JIT will warm up during normal processing, so there's very little advantage to introduce additional code just for that. In the worst case you might be introducing bugs, spending extra development time and even harming performance.

在正常的生产代码中,您很少会看到用于预热的代码。JIT 会在正常处理期间预热,因此为此引入额外的代码几乎没有什么好处。在最坏的情况下,您可能会引入错误、花费额外的开发时间甚至损害性能。

Unless you know for certain that you need some kind of warm-up, don't worry about it. The example application you described certainly doesn't need it.

除非您确定需要某种热身,否则不要担心。您描述的示例应用程序当然不需要它。

回答by Jens Schauder

Why JVM requires warmup?

为什么JVM需要预热?

Modern (J)VMs gather statistics at runtime about which code is used most often and how it is used. One (of hundreds if not thousands) example is optimization of calls to virtual functions (in C++ lingo) which have only on implementation. Those statistics can by their definition only gathered at run time.

现代 (J) 虚拟机在运行时收集有关哪些代码最常使用以及如何使用的统计信息。一个(数百个如果不是数千个)示例是对仅具有实现的虚函数(在 C++ 行话中)的调用的优化。根据他们的定义,这些统计信息只能在运行时收集。

Class loading itself is part of the warm up as well, but it obviously automatically happens before the execution of code inside those classes, so there is not much to worry about

类加载本身也是热身的一部分,但它显然会在这些类中的代码执行之前自动发生,因此无需担心

Which parts of the code should you warmup?

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

The part that is crucial for the performance of your application. The important part is to "warm it up" just the same way as it is used during normal usage, otherwise the wrong optimizations will be done (and undone later on).

对应用程序性能至关重要的部分。重要的部分是“预热”,就像在正常使用期间使用的一样,否则将进行错误的优化(并在以后撤消)。

Even if I warmup some parts of the code, how long does it remain warm (assuming this term only means how long your class objects remain in-memory)?

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

This is really hard to say basically the JIT compiler constantly monitors execution and performance. If some threshhold is reached it will try to optimize things. It will then continue to monitor performance to verify that the optimization actually helps. If not it might unoptimize the code. Also things might happen, that invalidate optimizations, like loading of new classes. I'd consider those things not predictable, at least not based on a stackoverflow answer, but there are tools the tell you what the JIT is doing: https://github.com/AdoptOpenJDK/jitwatch

这真的很难说,基本上 JIT 编译器会不断监控执行和性能。如果达到某个阈值,它将尝试优化事物。然后它将继续监视性能以验证优化是否确实有帮助。如果不是,它可能会未优化代码。也可能发生一些事情,使优化无效,例如加载新类。我认为这些事情是不可预测的,至少不是基于 stackoverflow 的答案,但是有一些工具可以告诉您 JIT 在做什么:https: //github.com/AdoptOpenJDK/jitwatch

How does it help if I have objects which need to be created each time I receive an event.

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

One simple example could be: you create objects inside a method, since a reference leaves the scope of the method, those objects will get stored on the heap, and eventually collected by the garbage collector. If the code using those objects is heavily used, it might end up getting inlined in a single big method, possibly reordered beyond recognition, until these Objects only live inside this method. At that point they can be put on the stack and get removed when the method exits. This can save huge amounts of garbage collection and will only happen after some warm up.

一个简单的例子可能是:您在方法内创建对象,因为引用离开了方法的范围,这些对象将存储在堆中,并最终被垃圾收集器收集。如果使用这些对象的代码被大量使用,它最终可能会被内联到一个大方法中,可能会重新排序无法识别,直到这些对象只存在于这个方法中。那时它们可以被放入堆栈并在方法退出时被删除。这可以节省大量的垃圾收集,并且只有在一些热身之后才会发生。

With all that said: I'm skeptical on the notion that one needs to do anything special for warming up. Just start your application, and use it and the JIT compiler will do it's thing just fine. If you experience problems, then learn what the JIT does with your application and how to fine tune that behavior or how to write your application so that it benefits the most.

尽管如此:我对人们需要做任何特别的事情来热身的观点持怀疑态度。只需启动您的应用程序,并使用它,JIT 编译器就会做得很好。如果您遇到问题,请了解 JIT 对您的应用程序的作用以及如何微调该行为或如何编写您的应用程序以使其受益最大。

The only case where I actually know about the need for warm up are benchmarks. Because if you neglect it there you will get bogus results almost guaranteed.

我真正知道需要热身的唯一情况是基准测试。因为如果你在那里忽视它,你几乎可以保证得到虚假的结果。

回答by arcy

Which parts of the code should you warmup?

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

There is no answer to this question in general. It depends entirely on your application.

这个问题一般没有答案。这完全取决于您的应用程序。

Even if I warmup some parts of the code, how long does it remain warm (assuming this term only means how long your class objects remain in-memory)?

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

Objects remain in memory for as long as your program has a reference to them, absent any special weak-reference use or something similar. Learning about when your program "has a reference" to something can be a little more obscure than you might think at first glance, but it is the basis for memory management in Java and worth the effort.

只要您的程序有对它们的引用,对象就会一直保留在内存中,没有任何特殊的弱引用使用或类似的东西。了解您的程序何时“引用”某些东西可能比您乍一看的时候更加模糊,但它是 Java 中内存管理的基础,值得付出努力。

How does it help if I have objects which need to be created each time I receive an event.

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

This is entirely dependent on the application. There is no answer in general.

这完全取决于应用程序。一般没有答案。

I encourage you to study and work with Java to understand things like classloading, memory management, and performance monitoring. It takes some amount of time to instantiate an object, in general it takes more time to load a class (which, of course, is usually done far less often). Usually, once a class is loaded, it stays in memory for the life of the program -- this is the sort of thing that you should understand, not just get an answer to.

我鼓励您学习和使用 Java,以了解类加载、内存管理和性能监控等内容。实例化一个对象需要一些时间,通常加载一个类需要更多的时间(当然,通常很少这样做)。通常,一旦加载了一个类,它就会在程序的整个生命周期中都保留在内存中——这是您应该理解的事情,而不仅仅是得到答案。

There are also techniques to learn if you don't know them already. Some programs use "pools" of objects, instantiated before they're actually needed, then handed off to do processing once the need arises. This allows a time-critical portion of the program to avoid the time spent instantiating during the time-critical period. The pools maintain a collection of objects (10? 100? 1000? 10000?), and instantiate more if needed, etc. But managing the pools is a significant programming effort, and, of course, you occupy memory with the objects in the pools.

如果您还不了解它们,也可以学习一些技巧。一些程序使用对象的“池”,在实际需要它们之前实例化,然后在需要时移交给处理。这允许程序的时间关键部分避免在时间关键期间花在实例化上的时间。池维护一组对象(10?100?1000?10000?),并在需要时实例化更多,等等。但是管理池是一项重要的编程工作,当然,您会占用池中的对象的内存.

It would be entirely possible to use up enough memory to trigger garbage collection much more often, and SLOW THE SYSTEM YOU WERE INTENDING TO SPEED UP. This is why you need to understand how it works, not just "get an answer".

完全有可能使用足够的内存来更频繁地触发垃圾收集,并减慢您打算加速的系统。这就是为什么您需要了解它是如何工作的,而不仅仅是“得到答案”。

Another consideration -- by far most of the effort put into making programs faster is wasted, as in not needed. Without extensive experience with the application being considered, and/or measurement of the system, you simply do not know where (or whether) optimization will even be noticeable. System/program design to avoid pathological cases of slowness ARE useful, and don't take nearly the time and effort of 'optimization'. Most of the time it is all any of us need.

另一个考虑因素——到目前为止,为使程序更快而付出的大部分努力都被浪费了,因为不需要。如果没有对所考虑的应用程序和/或系统测量的丰富经验,您根本不知道优化在哪里(或是否)甚至会引人注目。系统/程序设计,以避免缓慢的病理情况是有用的,并且不需要花费几乎“优化”的时间和精力。大多数时候,我们每个人都需要它。

-- edit -- add just-in-time compilation to the list of things to study and understand.

-- 编辑 -- 将即时编译添加到要学习和理解的内容列表中。

回答by Cootri

It is all about JITcompiler, which is used on the JVMto optimize bytecode in the runtime (because javaccan't use advanced or agressive optimization technics due to platform-independent nature of the bytecode)

这完全是关于JIT编译器,它用于在JVM运行时优化字节码(因为字节码的javac平台无关性,不能使用高级或积极的优化技术)

  1. you can warmup the code that will process your messages. Actually, in most cases you don't need do to it by special warm-up cycles: just let the application to start and process some of the first messages - JVMwill try to do its best to analyse code execution and make optimizations :) Manual warm-up with fake samples can yield even worse results

  2. code will be optimized after some amount of time and will be optimized until some event in the program-flow would degradate code state (after it JITcompiler will try to optimize the code again - this process never ends)

  3. short-living objects are subjects to be optimized too but generally it should help your message processing tenured code to be more efficient

  1. 您可以预热将处理您的消息的代码。实际上,在大多数情况下,您不需要通过特殊的预热周期来处理它:只需让应用程序启动并处理一些第一条消息 -JVM将尽力分析代码执行并进行优化:) 手动用假样本热身会产生更糟糕的结果

  2. 代码将在一段时间后被优化,并且将被优化直到程序流中的某个事件会降低代码状态(在它之后JIT编译器将再次尝试优化代码 - 这个过程永远不会结束)

  3. 短暂的对象也是需要优化的主题,但通常它应该有助于您的消息处理终身代码更高效

回答by birdspider

I always pictured it like the following:

我总是像下面这样描绘它:

You as (a C++ developer) could imagine an automatediterative approach by the jvmcompiling/hotloading/replacing various bits an pieces with (the imaginary analog of) gcc -O0,-O1,-O2,-O3variants (and sometimes reverting them if it deems it neccessary)

您作为(C++ 开发人员)可以想象一种自动迭代方法,通过jvm编译/热加载/用(虚构的类比)gcc -O0, -O1, -O2,-O3变体替换各个部分(有时在认为必要时还原它们)

I'm sure this it not strictly what happens but might be an useful analogy for a C++ dev.

我确信这不是严格意义上的事情,但对于 C++ 开发人员来说可能是一个有用的类比。

On a standard jvm the times it takes for a snippet to be considered for jit is set by -XX:CompileThresholdwhich is 1500 by default. (Sources and jvm versions vary - but I think thats for jvm8)

在标准 jvm 上,将片段考虑用于 jit 所需的时间-XX:CompileThreshold默认设置为 1500。(来源和 jvm 版本各不相同 - 但我认为这适用于 jvm8)

Further a bookwhich I have at hand states under Host Performace JIT Chapter (p59) that the following optimizations are done during JIT:

此外,我手头的一本书在主机性能 JIT 章节 (p59) 中指出,以下优化是在 JIT 期间完成的:

  • Inlining
  • Lock elimination
  • Virtual call elimination
  • Non-volatile memory write elimination
  • Native code generation
  • 内联
  • 锁消除
  • 虚拟呼叫消除
  • 非易失性存储器写消除
  • 原生代码生成


EDIT:

编辑:

regarding comments

关于评论

I think 1500 may be just enough to hint to JIT that it should compile the code into native and stop interpreting. would you agree?

我认为 1500 可能足以提示 JIT 它应该将代码编译为本机并停止解释。你同意吗?

I don't know if its just a hint, but since openjdk is open-source lets look at the various limits and numbers in globals.hpp#l3559@ver-a801bc33b08c(for jdk8u)

我不知道这是否只是一个提示,但由于 openjdk 是开源的,让我们看看globals.hpp#l3559@ver-a801bc33b08c(对于 jdk8u)中的各种限制和数字

(I'm not a jvm dev this might be the completly wrong place to look)

(我不是 jvm 开发人员,这可能是完全错误的地方)

Compiling a code into native does not necessarily mean it is also optimized.

将代码编译为本机并不一定意味着它也被优化了。

To my understanding - true; especially if you mean -Xcomp(force compile) - this blogeven states that it prevents the jvm from doing any profiling - hence optimizing - if you do not run -Xmixed(the default).

据我所知 - 真实; 特别是如果你的意思是-Xcomp(强制编译)——这个博客甚至指出它会阻止 jvm 进行任何分析——因此优化——如果你不运行-Xmixed(默认)。

So a timer kicks in to sample frequently accessed native code and optimize the same. Do you know how we can control this timer interval?

因此,计时器开始对经常访问的本机代码进行采样并对其进行优化。你知道我们如何控制这个定时器间隔吗?

I really don't know the details, but the gobals.hppI linked indeed defines some frequency intervals.

我真的不知道细节,但gobals.hpp我链接确实定义了一些频率间隔。