PRT memory usage

Thomas Schatzl thomas.schatzl at oracle.com
Mon Aug 21 16:42:18 UTC 2017


Hi,

On Sun, 2017-08-20 at 08:57 +0000, Milan Mimica wrote:
> Hello
> 
> I'm (still) trying to figure our high memory usage by PRT. I have
> studied heapRegionRemSet.cpp code and came up with a calculation I
> need you to confirm. Given region size of 8M and default G1 settings:
> * max number of PRT instances is 2^12 for each HR

Where did you get the 2^12 from?

> * PRT size is 64MB

Not completely following, where from do you understand that max prt
size is 64MB?

8M region size implies a 2k PRT (bitmap) size (one bit per 512 bytes)

> 
> I'm looking at the worst-case scenario and I don't dare to multiply
> the tree numbers. The cap is just to high. Am I missing something?
> Do only Young HR get a OtherRegionsTable?

All regions get them. But there are no PRTs from young regions to old
regions. Humongous continues regions don't have any remembered set.

> Where can I find the number of regions per generation? I cannot spot
> it in GC logs.

-Xlog:gc+heap=info gives you the following kind of output:

[15.369s][info ][gc,heap        ] GC(0) Eden regions: 3->0(576)
[15.369s][info ][gc,heap        ] GC(0) Survivor regions: 0->0(0)
[15.369s][info ][gc,heap        ] GC(0) Old regions: 0->1
[15.369s][info ][gc,heap        ] GC(0) Humongous regions: 0->0

Here is some rough calculation for remembered set size for jdk8
(copy&pasted from some other email from some time ago - I did not check
it again, so there might be some omissions):

"[...]
for every region in #non-young-regions:
  e1) coarse table: one bit for every #regions +
MAX(
     e2) sparse table: (2 * #regions) * (8 + #sparse-entries * 4)
     e3) fine table: MIN(#fine-entries, #non-young-regions - 1) * (bit
per #cards-per-region + 8 + 56)
  )

where:
#regions = maximum number of regions in heap = -Xmx / G1HeapRegionSize
#cards-per-region = G1HeapRegionSize / 512
#non-young-regions = #regions - #minimum-number-of-young-regions
#minimum-number-of-regions = #regions * G1NewSizePercent / 100
#sparse-entries = G1RSetSparseRegionEntries
#fine-entries = MIN(G1RSetRegionEntries, #non-young-regions)

Default values of options:
G1NewSizePercent = 5
G1RSetSparseRegionEntries = G1RSetSparseRegionEntriesBase *
(log2(#G1HeapRegionSize-in-MB) + 1)
G1RSetSparseRegionEntriesBase = 4

This is I think a reasonably accurate worst case memory usage.

[...]

The remembered set holds the greatest potential (but also the largest
risk to introduce performance issues) for savings.

This is due to the fact that actually every entry in a remembered set
(there, every region A stores for every other region x_0, ..., x_n the
cards that contain references to A) can either take memory in e1, e2,
or e3 (actually only the latter two for simplicity).

I.e. G1 can represent such an x_i in one of the data structures of e1,
e2, and e3, with different memory/performance characteristics.

The direction of representation is e2, then e3 and finally e1
(unfortunately there is no way to go back; when a region is freed, its
remembered set goes back to e2).

So what could be tried is trying to keep x_i at e2 level - that can be
controlled by G1RSetSparseRegionEntries. A given value of
G1RSetSparseRegionEntries indicates how many cards it can store - if
that is not enough, it expands to e3. (Just for reference:
G1RSetRegionEntries determines how many of e3 a remembered set for a
region A can hold - if more, it selects one to evict to e1; this is the
dreaded "coarsening" if you ever heard of it. Setting
G1RSetRegionEntries to something >= the number of regions avoids that
coarsening)

Since e2 is slightly more memory efficient, it might be worth trying to
bump G1RSetSparseRegionEntries.

If you think all what I explained above is gibberish to you, and
you are still interested, I can try to explain it a bit better :)

JDK9 made e2 more memory-efficient (only takes 1/4th of memory as
before), and does not create e1 if not needed.

[...]"

Hth,
  Thomas


More information about the hotspot-gc-use mailing list