-XX:G1ReservePercent and to-space exhausted
Asked Answered
S

1

17

I'm trying to understand what the -XX:G1ReservePercent actually does. The descriptions I found in the official documentation are not really comprehensive:

Sets the percentage of reserve memory to keep free so as to reduce the risk of to-space overflows. The default is 10 percent. When you increase or decrease the percentage, make sure to adjust the total Java heap by the same amount.

and the desciption of to-space exhausted log entry is this:

When you see to-space overflow/exhausted messages in your logs, the G1 GC does not have enough memory for either survivor or promoted objects, or for both.

[...]

To alleviate the problem, try the following adjustments:

Increase the value of the -XX:G1ReservePercent option (and the total heap accordingly) to increase the amount of reserve memory for "to-space".

[...]

Judging by the quote the to-space exhausted means that when performing mixed evacuation we do not have enough a free region to move survivors to.

But this then contradicts to the following official tuning advice in case of Full GC (emphasize mine):

Force G1 to start marking earlier. G1 automatically determines the Initiating Heap Occupancy Percent (IHOP) threshold based on earlier application behavior. If the application behavior changes, these predictions might be wrong. There are two options: Lower the target occupancy for when to start space-reclamation by increasing the buffer used in an adaptive IHOP calculation by modifying -XX:G1ReservePercent;

So what is the buffer and what does setting -XX:G1ReservePercent do (From the first glance AdaptiveIHOP has nothing to do with it...) ?

Does it keep some heap space always reserved so when mixed evacuation occur we always have free regiong to move survivors to?

Or the space is used for G1 internal housekeeping tasks? If so it is not clear what data the to-space contains so it exhausted?

Schutt answered 24/6, 2019 at 12:11 Comment(0)
M
8

To me, understanding what it really does, means to go the source code. I chose jdk-15.

The best description of this flag is here:

It determines the minimum reserve we should have in the heap to minimize the probability of promotion failure.

Excellent, so this has to do with "promotion failures" (whatever those are). But according to you quote in bold, this has something to do with AdaptiveIHOP also? Well yes, this parameter matters only in AdaptiveIHOP (which is on by default).

Another thing to notice is that G1ReservePercentage has no guarantee to be maintained all the time, it is a best effort.

If you look at how it is used in the very next line:

if (_free_regions_at_end_of_collection > _reserve_regions) {
    absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions;
}

things start to make some sense (for that bold statement). Notice how _reserve_regions are extracted for some computation. G1 will reserve that space for "promotion failures" (we will get to that).

If G1 reserves that space, it means less space is available for actual allocations. So if you "increase this buffer" (you make G1ReservePercent bigger as the quote suggests), your space for new objects becomes smaller and as such the time when GC needs to kick in will come sooner, so the time for when space reclamation needs to happen will come sooner too ("Lower the target occupancy for when to start space-reclamation..."). It is one complicated sentence, but in simple words it means that :

If you increase G1ReservePercentage, space will be needed to be reclaimed faster (more often GC calls).

Which, to be fair, is obvious. Not that I agree that you should increase that value, but this is what that sentence says.


After a certain GC cycle, be that minor, mixed or major, G1 knows how many regions are free:

_free_regions_at_end_of_collection = _g1h->num_free_regions();

Which of course, is the length of "free list":

return _free_list.length();

Based on this value and _reserve_regions (G1ReservePercent) plus the target pause time (200 ms by default) it can compute how many regions it needs for the next cycle. By the time next cycle ends, there might be a case when there are no empty regions (or the ones that are empty can not take all the live Objects that are supposed to be moved). Where is Eden supposed to move live Objects (Survivor), or, if old region is fragmented - where are live objects supposed to be moved to defragmente? This is what this buffer is for.

It acts as a safety-net (the comments in the code make this far more easier to understand). This is needed in the hopes that it will avoid a Full GC. Because if there are no free regions (or enough) to move live Objects a Full GC needs to happen (most probably followed by a young GC also).


This value is usually known to be small when this message is present in logs. Either increase it, or much better give more heap to the application.

Mantis answered 7/10, 2020 at 3:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.