JDK-8196991 : Revamp G1 JMX MemoryPool and GarbageCollector MXBean definitions
  • Type: CSR
  • Component: hotspot
  • Sub-Component: gc
  • Priority: P4
  • Status: Draft
  • Resolution: Unresolved
  • Submitted: 2018-02-07
  • Updated: 2020-11-20
Related Reports
CSR :  
Description
 Summary
-------

The current JMX memory pool and collector definitions for the G1 collector don't reflect reality well, thus should be replaced with ones that do. I expect to ask for a backport to a JDK 11 update release, and possibly JDK 8, so users can transition their code independent of a JDK 8/10 to JDK 11 migration.

Problem
-------

See Summary.

Solution
--------

An attempt was made to merge old and new definitions in a compatible way, but foundered on the problem of iterating over G1s GarbageCollectorMXBeans: old and new versions would overlap functionality in ways that cannot be reconciled. The new definitions will be the default, but for compatibility, a new -XX:+G1UseLegacyMonitoring Hotspot switch will revert to the current definitions.

The three existing MemoryPoolMXBeans

```
G1 Eden Space     : eden
G1 Survivor Space : survivor space
G1 Old Gen        : old generation, including humongous regions
```

are replaced with six

```
G1 Eden Space     : eden
G1 Survivor Space : survivor space
G1 Old Space      : old generation (now a "space" rather than a "gen", and does not include regions containing humongous or archive objects)
G1 Humongous Space: humongous space (regions containing humongous objects)
G1 Archive Space  : class data sharing metadata space, a read-only space
G1 Free Space     : free space cache
```

Somewhat non-intuitively, the new G1 Free Space does *not* represent the part of the heap that is not in the other five spaces, rather it represents the part of the heap that G1 uses as a free region cache.

Both the new G1 Old Space and G1 Humongous Space have usage thresholds enabled. In the legacy model, only the G1 Old Gen has thresholds enabled.

The six new memory pools are address-wise disjoint, even though the G1 Archive Space is logically part of the G1 Old Space. Existing Java code that uses memory pools assumes that all memory pools are address-wise disjoint.

Three invariants exist for the Serial, Parallel and legacy G1 models.

1. The sum of the positive memory pool Usage.max sub-attributes is equal to the maximum available Java heap memory. it may not be exactly -Xmx, so we call it MaxHeapSize in this document. It is the value of the "max" property of the [MemoryUsage](https://docs.oracle.com/en/java/javase/15/docs/api/java.management/java/lang/management/MemoryUsage.html) result of calling [MemoryMXBean.getHeapMemoryUsage](https://docs.oracle.com/en/java/javase/15/docs/api/java.management/java/lang/management/MemoryMXBean.html#getHeapMemoryUsage()).
2. The sum of the positive Usage.max sub-attribute values minus the sum of the Usage.committed sub-attribute values is the total uncommitted memory.
3. The sum of all heap memory pools' positive max values minus the sum of their used values is the total free memory.

In order to conform to these invariants, the G1 legacy model sets all max values to undefined, except for G1 Old Gen, whose max value is MaxHeapSize.

In the new G1 model, the first and second invariants do not hold. Rather, memory pool max values are defined for the G1 Eden Space, G1 Old Space, G1 Archive Space, and G1 Free Space, and are undefined (i.e., -1) for the G1 Survivor Space and G1 Humongous Space. 

In both models, total committed memory is the value of the "committed" property of the [MemoryUsage](https://docs.oracle.com/en/java/javase/15/docs/api/java.management/java/lang/management/MemoryUsage.html) result of calling [MemoryMXBean.getHeapMemoryUsage](https://docs.oracle.com/en/java/javase/15/docs/api/java.management/java/lang/management/MemoryMXBean.html#getHeapMemoryUsage()). Uncommitted memory is just (MaxHeapSize - that "committed" value).

In the new G1 model, the third invariant does not hold because max values are space-specific. The current total free space, both reserved and committed, is (MaxHeapSize - the above "committed" value + the G1 Free Space committed value).

The committed and used sub-attribute values for each of the new G1 pools, other than the G1 Free Space, are the same, net of their regions' internal free space. used values may not include internal allocation region free space and are thus always less than or equal to committed values, which latter are an integral multiple of the region size. Inversely, the G1 Free Space used value may include allocation regions' internal free space, and thus will always be greater than or equal to its committed value.

If defined, max sub-attribute values are approximations. An attempt is made to make them be the maximum sizes that can be reached from the current heap state, so max values may vary over time.

Since the new G1 Archive Space is loaded during JVM initialization and does not change after that, its committed, used, and max values are the same as its init value.

For the initial implementation,

1. The G1 Eden Space max value is the value implied by G1MaxNewSizePercent and MaxHeapSize.
2. The G1 Survivor Space max value is undefined.
3. The G1 Old Space max value is (MaxHeapSize - the G1 Archive Space max value - the value implied by G1NewSizePercent and MaxHeapSize). The idea is that the G1 Survivor Space and G1 Humongous Space sizes could go to zero if all objects in them were tenured.
4. The G1 Archive Space max value is the same as its init value.
5. The G1 Humongous Space max value is undefined.
6. The G1 Free Space max value is (MaxHeapSize - the G1 Archive space max value - the value implied by G1NewSizePercent and MaxHeapSize). This is the the same definition as the one for G1 Old Space, but the idea here is that the heap could become completely empty, except for objects in G1 Archive Space and a minimal G1 Eden Space.

----------

The two existing GarbageCollectorMXBeans

```
G1 Young Generation : incremental collector, both pure young and mixed collections
G1 Old Generation   : stop-the-world full heap collector
```

are replaced with four. They and the memory pools they affect are

```
G1 Young : incremental pure young generation collector
    G1 Eden Space
    G1 Survivor Space
    G1 Humongous Space
    G1 Free Space
G1 Mixed : incremental young + partially old collector
    G1 Eden Space
    G1 Survivor Space
    G1 Humongous Space
    G1 Old Space
    G1 Free Space
G1 Full : stop-the-world full heap collector
    G1 Eden Space
    G1 Survivor Space
    G1 Humongous Space
    G1 Old Space
    G1 Archive Space
    G1 Free Space
G1 Concurrent Cycle : a concurrent cycle
    G1 Humongous Space
    G1 Old Space
    G1 Free Space
```

The CollectionTime attribute for the G1 Concurrent Cycle collector is the total of a concurrent cycle's stop-the-world wall clock time. An associated young collection is reported as a separate event.

Because the G1 Archive Space is logically part of the G1 Old Space, it is associated with the G1 Full GarbageCollector, but will never be collected.

----------

JFR currently identifies three garbage collectors, New, Old, and Full. These correspond to the G1 Young, G1 Mixed, and G1 Full collectors, respectively. There are no changes to JFR event definitions.

Specification
-------------

No JMX specification change is necessary, since the per-JVM ManagementFactory and per-collector GarbageCollector MXBeans MemoryPools are undefined by the specification.


Comments
Looks good to Java platform team at Google! We have faced the same issue, that the old interface is quite confusing. We look forward to using the new interface in JDK11.
18-10-2018

Split jstat counter revamp off from this CSR into JDK-8210966.
20-09-2018

I've updated the Description to reflect my comment.
14-02-2018

On backporting to JDK8, we could just change the JDK8 switch default to false for JDK8. For JDK8 programs that set the switch to true on the command line, there would be no change needed to migrate to JDK11 because the switch setting would be a nop in JDK11. JDK8 programs that go with the JDK8 default will have to explicitly set the switch to true to get legacy behavior in JDK11, just as with this spec. With this approach, there's no compatibility breakage in JDK8. I'm ok with archive space not overlapping old space. I forgot to explicitly say in the spec that the archive space is an on-heap pool. It's easy to assume that it's off-heap because it's not accessible from any GarbageCollector, only from the ManagementFactory. The way to compute long term heap occupancy then becomes iterating over all the memory pools obtained from ManagementFactory.getMemoryPoolMXBeans and including in the sum CollectionUsage for all pools except non-heap and eden. A small spec change request: imo the switch should be a G1-specific switch, i.e., defined in g1_globals.hpp. All of those switch names start with 'G1', so I'd like to change the switch name to G1UseLegacyMXBeans.
14-02-2018

Overall, I think the proposal looks much better than what we currently have, thumbs up! I have two comments about the proposal: - this should not be backported to JDK 8 IMO, the compatibility breakage is way too large for an update release. This work should IMO land in a major release, e.g. JDK 11 or JDK 12. - The memory pool "G1 Archive Space" should _not_ overlap "G1 Old Space", the memory areas are semantically very different ("G1 Archive Space" should only include archive regions and "G1 Old Space" should only include old regions, and archive regions and old regions are treated very differently by G1).
14-02-2018