第二章 Ergonomics
Ergonomics is the process by which the Java Virtual Machine (JVM) and garbage collection tuning, such as behavior-based tuning, improve application performance. The JVM provides platform-dependent default selections for the garbage collector, heap size, and runtime compiler. These selections match the needs of different types of applications while requiring less command-line tuning. In addition, behavior-based tuning dynamically tunes the sizes of the heap to meet a specified behavior of the application.
Ergonomics 是JVM GC调优的过程,比如基于行为的调优,提升应用程序性能。JVM提供了平台相关的默认选项,这些选项影响到GC,堆大小和运行时编译器。这些选项满足了不同类型的应用程序减少使用命令行参数进行调优的需求。基于行为的调优还能动态调整堆大小来满足应用程序特定的需求。
1. GC,堆和运行时编译默认选项
1.1. 服务器判定标准
l 2个或者更多内核
l 2G或者更多内存
1.2. 符合服务器的机器将执行如下默认选择
l Throughput garbage collector
l Initial heap size of 1/64 of physical memory up to 1 GB
l 初始堆大小为物理内存的1/64,最大1G
l Maximum heap size of 1/4 of physical memory up to 1 GB
l 最大堆大小为物理内存的1/4,最大1G
l Server runtime compiler
平台 |
操作系统 |
默认 |
服务器默认 |
i586 |
Linux |
客户端 |
服务器 |
i586 |
Windows |
客户端 |
客户端 |
SPARC 64位 |
Solaris |
服务器 |
服务器 |
AMD 64位 |
Linux |
服务器 |
服务器 |
AMD64位 |
Windows |
服务器 |
服务器 |
1.3. 基于行为的调优Behavior-Based Tuning
For the parallel collector, Java SE provides two garbage collection tuning parameters that are based on achieving a specified behavior of the application: maximum pause time goal and application throughput goal; see the section The Parallel Collector. (These two options are not available in the other collectors.) Note that these behaviors cannot always be met. The application requires a heap large enough to at least hold all of the live data. In addition, a minimum heap size may preclude reaching these desired goals.
对于并行式收集器,Java SE提供了两个gc的调优参数,这两个gc参数都是基于应用程序特定行为的:最大暂停时间目标和应用程序吞吐量目标。请参考并行GC(这俩参数其他类型的GC不能用)。注意这两种行为的目标不能同时被满足。应用程序需要足够大的堆内存来持有活跃数据,因此,一个过小的堆内存,会减少达成这些目标的几率。
1.3.1. 最大暂停时间Maximum Pause Time Goal
The pause time is the duration during which the garbage collector stops the application and recovers space that is no longer in use. The intent of the maximum pause time goal is to limit the longest of these pauses. An average time for pauses and a variance on that average is maintained by the garbage collector. The average is taken from the start of the execution but is weighted so that more recent pauses count more heavily. If the average plus the variance of the pause times is greater than the maximum pause time goal, then the garbage collector considers that the goal is not being met.
暂停时间是gc暂时停止应用程序的执行,回收不再使用的空间的时长。设定最大暂停时间目标的意图是限制暂停时间的长度。gc会维护一个暂停时间的均值和这个均值的抖动。平均值从gc开始执行就计算,但是最近发生gc的时间会占较大权重。如果均值加上抖动数值比配置项里面的最大暂停时间还要长,那么gc就会认为目标没有达成。
The maximum pause time goal is specified with the command-line option -XX:MaxGCPauseMillis=<nnn>. This is interpreted as a hint to the garbage collector that pause times of <nnn> milliseconds or less are desired. The garbage collector will adjust the Java heap size and other parameters related to garbage collection in an attempt to keep garbage collection pauses shorter than <nnn> milliseconds. By default there is no maximum pause time goal. These adjustments may cause garbage collector to occur more frequently, reducing the overall throughput of the application. The garbage collector tries to meet any pause time goal before the throughput goal. In some cases, though, the desired pause time goal cannot be met.
要规定最大暂停时间目标,需要使用命令行参数 -XX:MaxGCPauseMillis=<nnn>。这个命令告诉gc暂停时间要少于等于<nnn>规定的毫秒数。gc将调整Java堆的大小和其他gc相关的参数,来试图达成这个目标。默认是没有最大暂停时间这个目标的。规定这个参数,有可能引发频繁的gc,减少程序的总体吞吐量。gc对待最大暂停时间的目标的优先级要高过吞吐量目标。
1.3.2. 吞吐量Throughput Goal
The throughput goal is measured in terms of the time spent collecting garbage and the time spent outside of garbage collection (referred to as application time). The goal is specified by the command-line option -XX:GCTimeRatio=<nnn>. The ratio of garbage collection time to application time is 1 / (1 + <nnn>). For example, -XX:GCTimeRatio=19 sets a goal of 1/20th or 5% of the total time for garbage collection.
吞吐量目标是使用花费在gc上的时间和在gc之外程序的运行时间的比值来度量的。这个目标由命令行参数-XX:GCTimeRatio=<nnn>规定。gc的时间和程序运行时间的比例计算公式为
1 / (1 + <nnn>)。比如-XX:GCTimeRatio=19,设定了一个目标,该目标告诉jvm,使用1/20,也就是5%的时间来进行垃圾回收。
The time spent in garbage collection is the total time for both the young generation and old generation collections combined. If the throughput goal is not being met, then the sizes of the generations are increased in an effort to increase the time that the application can run between collections.
gc的时间,是指花费在年轻代和老生代回收的时间的总和。如果吞吐量目标没有被满足,那么各代的堆内存将被增加,这样可使应用程序在两次gc之间运行更长的时间。
1.3.3. Footprint Goal
If the throughput and maximum pause time goals have been met, then the garbage collector reduces the size of the heap until one of the goals (invariably the throughput goal) cannot be met. The goal that is not being met is then addressed.
如果最大暂停时间目标和吞吐量目标同时被满足,那么gc将会减少堆内存的大小直到其中之一不能被满足(几乎总是吞吐量首先不被满足)。未被满足的目标稍后被解决。
2. 调优策略Tuning Strategy
Do not choose a maximum value for the heap unless you know that you need a heap greater than the default maximum heap size. Choose a throughput goal that is sufficient for your application.
不要选择设定堆的最大值,除非你清楚的知道你需要一个比默认最大堆大小更大的堆内存。对你的应用来说。选择设定吞吐量目标对你的应用程序来说足够了。
The heap will grow or shrink to a size that will support the chosen throughput goal. A change in the application's behavior can cause the heap to grow or shrink. For example, if the application starts allocating at a higher rate, the heap will grow to maintain the same throughput.
支持吞吐量目标的设定,堆内存会自动增加或者减少。应用程序行为的变化能导致堆增加或者减少。比如,一个程序启动时会以一个高比例进行空间分配,堆就会随之增长。
If the heap grows to its maximum size and the throughput goal is not being met, the maximum heap size is too small for the throughput goal. Set the maximum heap size to a value that is close to the total physical memory on the platform but which does not cause swapping of the application. Execute the application again. If the throughput goal is still not met, then the goal for the application time is too high for the available memory on the platform.
如果堆增长到了极限尺寸,吞吐量目标还是没有被满足,那就说明最大堆内存的设定相对于这个目标来说太小了。将最大堆内存设定到操作系统物理内存极限,但是请小心不要引发虚存交换。重启应用,如果吞吐量目标还是没有被满足,那就是说这个目标订的偏高了。
If the throughput goal can be met, but there are pauses that are too long, then select a maximum pause time goal. Choosing a maximum pause time goal may mean that your throughput goal will not be met, so choose values that are an acceptable compromise for the application.
如果吞吐量目标满足了,但是暂停的时间太长,那么就选择设定最大暂停时间目标。但是选择设定最大暂停时间往往意味着吞吐量目标得不到满足,因此要好好平衡下。
It is typical that the size of the heap will oscillate as the garbage collector tries to satisfy competing goals. This is true even if the application has reached a steady state. The pressure to achieve a throughput goal (which may require a larger heap) competes with the goals for a maximum pause time and a minimum footprint (which both may require a small heap).
在满足gc的两个互相竞争的指标面前设定堆内存的大小是个让人纠结的问题。即便应用程序达到了一个稳定运行的状态的时候也会面对这样的困难。达到最大吞吐量指标(需要大的堆内存)的压力常常会和最大暂停时间(需要小的堆内存设定)及最小空间占用这样的目标相冲突。
第三章 Generations
One strength of the Java SE platform is that it shields the developer from the complexity of memory allocation and garbage collection. However, when garbage collection is the principal bottleneck, it is useful to understand some aspects of this hidden implementation. Garbage collectors make assumptions about the way applications use objects, and these are reflected in tunable parameters that can be adjusted for improved performance without sacrificing the power of the abstraction.
Java平台的优点之一就是对开发者屏蔽内存分配和gc的复杂性。然而,当gc成为主要瓶颈的时候,了解一些gc隐藏实现的内容是十分有必要的。gc对应用程序使用对象的方式做了一些预先假设,这反映在调优参数中,这些参数调整之后可以提升性能而又不失抽象的灵活性。
An object is considered garbage when it can no longer be reached from any pointer in the running program. The most straightforward garbage collection algorithms iterate over every reachable object. Any objects left over are considered garbage. The time this approach takes is proportional to the number of live objects, which is prohibitive for large applications maintaining lots of live data.
当一个对象,在运行的程序中,从任意一个指针都不可达的时候,就会被认为是垃圾。最直白的gc算法就是遍历每一个可达的对象。任何被剩下的对象都被认作垃圾。这种算法的耗时和生存对象的数量有关,当面对超大型程序持有海量对象时,开销过高。
The virtual machine incorporates a number of different garbage collection algorithms that are combined using generational collection. While naive garbage collection examines every live object in the heap, generational collection exploits several empirically observed properties of most applications to minimize the work required to reclaim unused (garbage) objects. The most important of these observed properties is the weak generational hypothesis, which states that most objects survive for only a short period of time.
虚拟机采用不同的算法对内存进行分代回收。尽管gc还是对堆里面每一个对象都逐一检查,但是通过经验观察法,分代回收的策略观察到大部分应用程序的一些性质,利用这些性质,可以大大减少回收垃圾对象的负担。这些被观察到的性质中,最重要的特性就是弱代假设,即大部分的对象生存的周期都很短。
The blue area in Figure 3-1, "Typical Distribution for Lifetimes of Objects" is a typical distribution for the lifetimes of objects. The x-axis is object lifetimes measured in bytes allocated. The byte count on the y-axis is the total bytes in objects with the corresponding lifetime. The sharp peak at the left represents objects that can be reclaimed (in other words, have "died") shortly after being allocated. Iterator objects, for example, are often alive for the duration of a single loop.
Figure 3-1 Typical Distribution for Lifetimes of Objects
Description of "Figure 3-1 Typical Distribution for Lifetimes of Objects"
Some objects do live longer, and so the distribution stretches out to the right. For instance, there are typically some objects allocated at initialization that live until the process exits. Between these two extremes are objects that live for the duration of some intermediate computation, seen here as the lump to the right of the initial peak. Some applications have very different looking distributions, but a surprisingly large number possess this general shape. Efficient collection is made possible by focusing on the fact that a majority of objects "die young."
有效率的回收算法应该聚焦于这样一个事实:大部分的对象在创建之初就死了。
To optimize for this scenario, memory is managed in generations (memory pools holding objects of different ages). Garbage collection occurs in each generation when the generation fills up. The vast majority of objects are allocated in a pool dedicated to young objects (the young generation), and most objects die there. When the young generation fills up, it causes a minor collection in which only the young generation is collected; garbage in other generations is not reclaimed. Minor collections can be optimized, assuming that the weak generational hypothesis holds and most objects in the young generation are garbage and can be reclaimed. The costs of such collections are, to the first order, proportional to the number of live objects being collected; a young generation full of dead objects is collected very quickly. Typically, some fraction of the surviving objects from the young generation are moved to the tenured generation during each minor collection. Eventually, the tenured generation will fill up and must be collected, resulting in a major collection, in which the entire heap is collected. Major collections usually last much longer than minor collections because a significantly larger number of objects are involved.
为了优化上图的场景,内存被分代管理(内存池里持有的对象按照不同的时期划分)。gc在每一个代被填满的时候发生,并且发生在每个代内。大多数的对象被分配在一个为年轻对象准备的内存池里(年轻代),并且大部分的对象也死在这里。当年轻代被占满的时候,会触发minor gc,minor gc仅回收年轻代的内存,不回收其他代的内存。基于弱代假设的前提:大部分在年轻代的对象是垃圾,并且可以被回收,minor gc是有优化的余地的。Minor gc的开销和年轻代存活的对象数量成正比,如果年轻代的对象全是死对象,那么回收起来是很快的。一般情况下,某些在年轻代存活的对象,在minor gc后,将被从年轻代移动到成熟代。最终,成熟代也会被占满,需要回收,导致发生major gc,在major gc中,整个堆将被回收。Major gc比minor gc持续的时间要长很多,因为major gc涉及到全局所有的对象。
As noted in the section Ergonomics, ergonomics selects the garbage collector dynamically to provide good performance on a variety of applications. The serial garbage collector is designed for applications with small data sets, and its default parameters were chosen to be effective for most small applications. The parallel or throughput garbage collector is meant to be used with applications that have medium to large data sets. The heap size parameters selected by ergonomics plus the features of the adaptive size policy are meant to provide good performance for server applications. These choices work well in most, but not all, cases, which leads to the central tenet of this document:
如同在第二章Ergonomics开头中提到的,针对不同的应用,Ergonomics会动态选择gc来提供好的性能。串行gc适合小量数据集合,默认的参数设定也适合大多数小型程序。并行或者吞吐量gc适合中到大型数据集合。Ergonomics通过选择设定合适的堆的初始大小,加上动态调整策略,可以为不同的应用程序提供较好的性能保证。大部分情况下这种机制工作量好,但并不是所有情况都一帆风顺,这也就引出了本文的主旨:
If garbage collection becomes a bottleneck, you will most likely have to customize the total heap size as well as the sizes of the individual generations. Check the verbose garbage collector output and then explore the sensitivity of your individual performance metric to the garbage collector parameters.
如果gc变成了瓶颈,你将不得不亲自定制堆的大小,并仔细的设定每个单独的代的大小。检查gc的详细输出信息,采用你程序最为性能敏感的参数设定。
Figure 3-2, "Default Arrangement of Generations, Except for Parallel Collector and G1" shows the default arrangement of generations (for all collectors with the exception of the parallel collector and G1):
Figure 3-2 Default Arrangement of Generations, Except for Parallel Collector and G1
Description of "Figure 3-2 Default Arrangement of Generations, Except for Parallel Collector and G1"
At initialization, a maximum address space is virtually reserved but not allocated to physical memory unless it is needed. The complete address space reserved for object memory can be divided into the young and tenured generations.
在初始化的时候,一个最大的地址空间被保留,但是仅在需要的时候才会分配物理地址。整个内存地址空间被划分为年轻代和成熟代。
The young generation consists of eden and two survivor spaces. Most objects are initially allocated in eden. One survivor space is empty at any time, and serves as the destination of any live objects in eden; the other survivor space is the destination during the next copying collection. Objects are copied between survivor spaces in this way until they are old enough to be tenured (copied to the tenured generation).
年轻代包含一个eden区和两个survivor区。大部分对象初始被分配在eden区。其中一个survivor区在任意时点始终是空的,时刻准备着在下一次回收中拷贝另外一个survivor区存活的对象(这种空区拷贝可以有效保持对象紧凑并且避免删除操作,回收的时候,只需要把存活的对象紧凑的搬到另外一个区域然后一股脑儿的将本区域置为空即可,效率高)