Phrases like "unsustainable memory allocation rate" and "you need to maintain a low memory allocation rate" look like a proprietary term for Java champions (Java Champions). Complex, scary, and full of mysterious colors.
These words often appear, but if you dig deeper into these concepts, the mystery of it disappears. This article will try to uncover the mysteries of these terms.
What is the memory allocation rate? Why should we care about it?
The memory allocation rate refers to the total amount of memory allocated per unit of time, usually expressed in mb/sec. However, if you are happy, you can also use Pb/year to express. This is the whole thing-less mysterious, just the size of the memory allocations in Java code for a certain period of time.
But it doesn't make much sense to just know that. If you can endure, I will take you to apply this concept in practice. High allocation means that your program has a performance problem. From a practical point of view, the main impact is to make GC (garbage Collection) a bottleneck. From a hardware perspective, even the most commonly used hardware can support a few gb/sec per core allocation rate. In fact, your distribution rate will not exceed 1 gb/sec/core. So you can rest assured that hardware is unlikely to be an application bottleneck.
So, when we focus on GC, we can compare it to the real situation-if you create a lot of members, then you need to do a lot of cleanup work. We know that the JVM establishes a garbage collection mechanism that needs to know the memory allocation rate, thus changing the frequency of GC execution and the time of GC pauses.
Measurement of memory allocation rate
We begin to measure the memory allocation rate. We set the JVM parameter:-xx:+printgcdetails-xx:+printgctimestamps to open the GC log. Now the JVM begins to record the GC pause log in the following ways:
0.291: [GC (Allocation Failure) [psyounggen:33280k->5088k (38400K)] 33280k->24360k (125952K), 0.0365286 secs] [ times:user=0.11 sys=0.02, real=0.04 secs]
0.446: [GC (Allocation Failure) [psyounggen:38368k->5120k (71680K)] 57640k->46240k (159232K), 0.0456796 secs] [ times:user=0.15 sys=0.02, real=0.04 secs]
0.829: [GC (Allocation Failure) [psyounggen:71680k->5120k (71680K)] 112800k->81912k (159232K), 0.0861795 secs] [ times:user=0.23 sys=0.03, real=0.09 secs]
Based on the GC log above, we can calculate the allocation rate from the size of the previous collection of young Generation and the size before the next recovery. Using the above example, we can extract the following information:
After the JVM starts for 291 milliseconds, the size of the loaded object is 33280K. After the first minor GC cleanup, the remaining object size for the young generation is 5088K.
After 446 milliseconds of startup, the space occupied by the young generation has grown to 38368K, and the next GC has been triggered, and after this GC, the space occupied by the younger generation has been reduced to 5120K.
After starting 829 milliseconds, the young generation size is 71680K,GC and then reduced again to 5120K.
This data is shown in the following table, with the calculated memory allocation rate added to the rear of the younger generation footprint:
Events |
Time |
before you add a young generation |
after adding the young generation |
is assigned |
Allocation Rate |
1st GC |
291ms |
33,280kb |
5,088kb |
33,280kb |
114mb/sec |
2nd GC |
446ms |
38,368kb |
5,120kb |
33,280kb |
215mb/sec |
3rd GC |
829ms |
71,680kb |
5,120kb |
66,560kb |
174mb/sec |
Total |
829ms |
N/A |
N/A |
133,120kb |
161mb/sec |
With this information, we can say that for this particular software, the memory allocation rate during the measurement is 161 mb/sec.
Impact analysis
Now, with this information, we can understand that changing the memory allocation rate can increase or decrease the frequency of GC pauses, thus affecting the throughput rate of the application. First and foremost, you should be aware that only the Minor GC cleans up the pauses of the younger generation to be affected. The old Generation cleanup, whether frequency or duration, is not directly affected by the distribution rate, but is affected by the rate of growth (promotion rate), which is the term discussed in the next article.
Knowing this, we just need to focus on the pauses of the Minor GC, and we should go a step further to understand the differences in the internal memory pools of the younger generation. Because memory allocations are carried out in the Eden area, we can look directly at the impact of the Eden area size on the allocation rate. So we can assume that as the Eden region grows, the frequency of minor GC pauses will decrease, and the application will be able to meet the faster allocation rate.
In fact, when we use different Eden area sizes (-xx:newsize-xx:maxnewsize and-xx:survivorratio parameters) to perform the same instance, we can see two different memory allocation ratios:
Set the Eden area to 100M and run the above example to reduce the memory allocation rate to 100mb/sec.
Add an Eden zone to 1GB, increasing the memory allocation rate to close to 200mb/sec.
If you're still wondering why this is right-assuming you reduce the frequency of applying thread GC, you can do more useful work. This allows more objects to be generated, thereby supporting a higher memory allocation rate.
Now, before you come to the conclusion that "the bigger the Eden, the better", you should be aware that the memory allocation rate may not directly correlate to the actual throughput rate of the app. And this is just a technical measurement that focuses on throughput. The memory allocation rate only affects how often the Minor GC pauses application threads, but from the overall impact you also need to consider the pauses of the Major GC and the business operations provided by the application.
What is the Java object allocation rate