The Definitive Guide to Java Performance - Summary 8

garbage collection algorithm

Understanding CMS Collectors

Tuning for concurrent mode failures

The most important thing to do when tuning the CMS collector is to avoid concurrent mode failures and promotion failures. As seen in the CMS garbage collection log, concurrent mode failures often occur because the CMS cannot clean up the old space fast enough: when the new generation needs to be garbage collected, the CMS collector calculates that the old space is not free enough Space can accommodate these promoted objects, and the old generation has to be garbage collected first. .

Initially, the objects in the old generation space are arranged neatly and orderly one by one. When the occupancy of the old generation space reaches a certain level (the default value is 70%), concurrent collection begins. When a CMS background thread starts scanning the old generation space, looking for useless garbage objects, the competition begins: the CMS collector must complete the scanning and recycling of the old generation space before the remaining space (30%) of the old generation is exhausted. If concurrent collection loses this speed race, a concurrent mode failure occurs in the CMS collector.

There are several ways to avoid this failure:

  • Find a way to increase the old generation space, either by moving only part of the new generation objects to the old generation, or by adding more heap space.
  • Run the background recycling thread at a higher frequency.
  • Use more background recycling threads.
自适应调优和CMS垃圾搜集
CMS收集器使用两个配置MaxGCPauseMllis=N和GCTimeRatio=N来确定使用多大的堆和多大的代空间。

CMS收集与其他的垃圾收集方法一个显著的不同是除非发生Full GC,否则CMS的新生代大小不会作调整。由于CMS的目标是尽量避免Full GC,
这意味着使用精细调优的CMS收集器的应用程序永远不会调整它的新生代大小。

程序启动时可能频发并发模式失效,因为CMS收集器需要调整堆和永久代(或者元空间)的大小。使用CMS收集器,初始时采用一个比较大
的堆(以及更大的永久代/元空间)是一个很好的主意,这是一个特例,增大堆的大小反而帮助避免了那些失效。

A better solution is to increase the heap size if more memory is available, otherwise try tweaking the way background threads run to fix this.

  1. Give background threads more chances to run

One way for the CMS collector to win this race is to start concurrent collection cycles earlier. Apparently, the CMS collector starts a concurrent cycle when the old generation space reaches 60%, which is more likely to complete the garbage collection than starting when the old generation space is 70%. In order to achieve this configuration, the easiest way is to set the following two flags at the same time: -XX:CMSInitiatingOccupancyFraction=Nand -XX:+UseCMSInitiatingoccupancyonly. Using these two parameters at the same time can help CMS make decisions more easily: if you set these two flags at the same time, then CMS will only decide when to start the background thread based on the set old age space occupancy. By default, UseCMSInitiatingoccupancyOnlythe value of the flag is false, and the CMS will use a more complex algorithm to determine when to start the parallel collection thread. If it is necessary to start the background thread early, it is recommended to use the simplest method, which is to UseCMSInitiatingOccupancyonlyset the value of the flag to true.

CMSInitiatingOccupancyFractionAdjustments to parameter values ​​may require multiple iterations to determine. If the flag is turned on UseCMSInitiatingoccupancyonly, CMSInitiatingoccupancyFractionthe default value is set to 70, that is, CMS will start a concurrent collection cycle when the old generation space reaches 70%.

For a particular application, a better value for this flag can be found from the GC log when the CMS cycle failed to start for the first time. The specific method is to look for the concurrent mode failure in the garbage collection log, and then reversely search for the latest startup record of the CMS cycle after finding it. The line containing CMS-initial-mark information in the log contains the occupancy of the old generation space when the CMS cycle starts, as follows:

89.976:[GC [1 CMS-initial-mark: 702254K(1398144K)]
				772530K(2027264K),0.0830120 secs]
				[Times:user=0.08 sys=0.00,real=0.08 secs]

In this example, according to the output of the log, we can judge that the occupancy rate of the old generation space is 50% at that moment (the size of the old generation space is 1398 MB, of which 702MB is occupied). However, this value is not early enough, so we need to adjust CMSInitiatingOccupancyFractionthe value to set it to something less than 50. (Although CMSInitiatingOccupancyFractionthe default value is 70, the flag is not enabled in this example UseCMSInitiatingoccupancyonly, so the CMS collector in the example starts the CMS background thread when the old generation space occupies 50%.)

After understanding CMSInitiatingOccupancyFractionthe working principle, you may have doubts, whether the parameter value can be set to 0 or other relatively small values, so that the background thread of the CMS can continue to run. Such a setup is generally not recommended, but if the trade-offs are well understood, appropriate compromises are acceptable.

The first trade-off here is CPU: CMS background threads run continuously, and they consume a lot of CPU clocks - each CMS background thread takes up 100% of a CPU while running. There will also be brief bursts when multiple CMS threads are running at the same time, and the total CPU usage of the machine will therefore skyrocket. If these threads continue to run for no purpose, it will only waste precious CPU resources in vain.

On the other hand, it's not that using too many CPU cycles is the problem. Background CMS threads have to run when needed, which is hard to avoid even in the best of circumstances. Therefore, the machine must reserve enough CPU cycles to run these CMS threads. So when planning the machine, you must consider leaving a margin for the use of this part of the CPU.

During the CMS cycle, if the CMS background thread is not running, can these CPU clocks be used to run other applications? Usually not. If there is another application using the same clock cycle, it has no way of knowing when the CMS thread will run. Therefore, the application thread and the CMS thread will compete for CPU resources, and this will likely cause the CMS thread to "lose its race". Sometimes, through complex operating system tuning, it is possible to allow the application thread to run in the same clock cycle at a lower priority than the CMS thread, but these methods are quite complicated and error-prone. So the answer is yes, the more frequently the CMS cycle runs, the longer the CPU cycles, and if not, those CPU cycles are idle.

The second trade-off is more important and it has to do with application stalls. As observed in the GC logs, CMS pauses all application threads at certain stages. The main purpose of using the CMS collector is to limit the impact of GC pauses, so frequently running more invalid CMS cycles is counterproductive. The pause time of CMS is much shorter than that of the new generation, and the application thread may not even feel these additional pauses-this is also a trade-off, whether to avoid additional pauses or to reduce concurrent mode failures probability. However, as mentioned earlier, the stalls caused by continuously running background GC threads may cause overall stalls, which will eventually reduce the performance of the application.

Unless these trade-offs are acceptable, do not set CMSInitiatingoccupancyFractionthe value of the parameter to more than the number of active data in the heap, at least 10% to 20% less.

  1. Adjust CMS background thread

Each CMS background thread will occupy 100% of a CPU on the machine. If an application suffers a concurrent mode failure and additional CPU cycles are available, -XX:ConcGCThreads=Na flag can be set to increase the number of background threads. By default, the value of ConcGCThreads is calculated from the value of the ParallelGCThreads flag:

	ConcGCThreads = (3 + ParallelGCThreads) / 4

The above calculation uses the integer calculation method, which means that if the value of ParallelGCThreads is between 1 and 4, the value of ConcGCThread is 1, if the value of ParallelGCThreads is between 5 and 8, the value of ConcGCThreads is 2, and so on .

The point of adjusting this flag is to determine whether there are CPU cycles available. If the ConcGCThreads flag value is set too high, garbage collection will take up CPU cycles that could be used to run application threads; in effect, this configuration will cause the application to stall slightly, because the application thread needs to wait to continue on the CPU again. Chances of running.

In addition, on a system with a large number of CPUs, the default value of the ConcGCThreads parameter may be too large. If you do not encounter frequent concurrent mode failures, you can consider reducing the number of background threads to release this part of CPU cycles for running application threads.

quick summary

  1. Avoiding concurrent mode failure is the key to improving the processing capacity of the CMS collector and obtaining high performance.
  2. The easiest way to avoid concurrent mode failures (if possible) is to increase the size of the heap.
  3. Otherwise, the next step we can take is to CMSInitiatingOccupancy-Fractionstart running concurrent background threads as early as possible by tuning parameters.
  4. In addition, adjusting the number of background threads can also help to solve this problem.

Guess you like

Origin blog.csdn.net/weixin_42583701/article/details/131098183