openjdk-proposals

Ergonomics Profiles

Ongoing PR: https://github.com/microsoft/openjdk-jdk/pull/9

Summary

Introduce a new JVM feature named Ergonomics Profiles, with a shared profile for the existing default ergonomics heuristics and a dedicated profile for when the JVM is running on systems with dedicated resources for the one JVM process, e.g., in containers and virtual machines where the JVM is the dominant process.

Goals

Reduce resource waste on dedicated environments while preserving legacy heuristics by:

  1. Introducing the concept of ergonomics profiles.
  2. Define existing ergonomics heuristics as shared and maintaining those as the default.
  3. Define a new set of ergonomics heuristics, named dedicated, designed for environments where the JVM is expected to be the dominant process.
  4. Allow the user to tell the JVM to automatically select between dedicated and shared profiles.

Non-Goals

Success Metrics

The dedicated profile, when active, should produce less resource waste with no performance regressions. This should hold for most workloads when compared to default heuristics in a dedicated environment.

Motivation

The original design of default JVM ergonomics and heuristics was aimed at traditional bare metal servers or large virtual machine environments where the JVM would share resources with other processes (e.g., a data store). Today, more than half of Cloud based JVM workloads are running in dedicated environments such as containers, followed by a second majority of workloads on virtual machines where many of those are being dedicated to the JVM.

A study in 2023 by New Relic, an APM vendor with access to millions of JVMs in production, identified that more than 70% of their customers’ JVMs were running inside dedicated environments. Many of these JVMs were running without explicit JVM tuning flags. Therefore, the JVM was running with default ergonomics traditionally aimed at shared environments. Under this condition, the JVM does not utilize most of the memory available. With underuse of available resources, application developers and operators tend to apply horizontal scaling to address performance issues. This premature move to horizontal scaling, in turn, leads to more resource waste, both in terms of computing resources and engineering resources.

In the study by New Relic, it was identified the following characteristics by JVM workloads running inside containers:

CPU usage

Number of cores % of workloads
1 36.25%
2 15.54%
4 5.75%
8 29.64%

Heap size | Memory amount (MB) | % of workloads | |——————–|—————-| | <= 512 | 24.39% | | <= 1024 | 21.11% | | <= 2048 | 20.69% | | <= 3072 | 14.01% | | <= 4096 | 5.94% | | <=8192 | 6.91% |

In the case of workloads in environments with more than 1792 MB of RAM and two or more processors, the current default heap size is 25% of available memory. This value is not aligned with users’ expectations, therefore requiring manual tuning of heap size. In environments with 2 or more processors and below 1792MB of RAM, the default ergonomics will still select SerialGC, which leads to higher pauses despite the JVM having access to more parallel computing.

By increasing JVM’s awareness to more resources available and expanding the garbage collector selection in dedicated environments, the JVM will have more opportunities to behave adequately, or at the very least meet the resource consumption expected by the user.

Description

This JEP proposes adding the concept of Ergonomics Profiles. The existing ergonomics will be placed under the shared profile. A second profile named dedicated is designed for when the JVM is aimed at environments with dedicated resources - such as, but not limited to, Linux containers and virtual machines – where the JVM is the dominant process. The default profile will be shared.

Selecting a profile

To select an ergonomic profile, the user must provide the following flag:

-XX:ErgonomicsProfile=<shared|dedicated>

Default profile

The shared ergonomics profile will be selected by default.

Auto profile selection

To instruct the JVM to automatically select an ergonomic profile, the user must provide the following flag:

-XX:+AutoErgonomicsProfile

When the auto mode is enabled, the JVM will activate the dedicated profile in cases it believes the resources are dedicated. This JEP proposes initial support for Linux containers detection only. If both automatic selection and a particular profile are set, the JVM startup must fail.

Shared profile

The shared profile represents the heuristics that the HotSpot JVM uses today.

Dedicated profile

The dedicated profile will contain different heuristics to minimize resource waste. Two primary characteristics will be adjusted: heap size (initial and maximum) and the garbage collector selected. Heap size allocation will be less conservative compared to shared, and garbage collector selection will consider more options.

Garbage Collector selection in dedicated profile

Memory Processors GC selected
Any 1 Serial
<=2048 MB >1 Parallel
>2048 MB >1 G1

Default initial heap size

The dedicated profile will set InitialRAMPercentage at 50%.

Default maximum heap size

The heap size percentage progressively grows based on the available memory to reduce waste on memory reserved for non-heap operations. This heap size percentage growth is based on common workloads observed in containers, such as REST-based web applications and microservices. If the percentage were to be the same, progressive waste would still occur.

Memory Max Heap size
< 0.5 GB 50%
>= 0.5 GB 75%
>= 4 GB 80%
>= 6 GB 85%
>= 16 GB 90%

Changes to RuntimeMXBean

An application can obtain the profile selection programmatically by reading the property java.vm.ergonomics.profile:

var ergonomicsProfile = System.getProperty("java.vm.ergonomics.profile");

The profile selection may also be obtained programmatically through JMX by the inclusion of the following method in java.management.RuntimeMXBean:

String getJvmErgonomicsProfile()

Testing

This proposal only affects ergonomics heuristics for when dedicated profile is active. This profile will be thoroughly performance tested with a variety of workloads commonly deployed in dedicated environments such as containers, with a variety of memory and CPU settings.

Risks and Assumptions

The dedicated profile may not be ideal for certain workloads where non-heap memory is required beyond assumed differences between total available memory and heuristics ergonomically selected. For these workloads, manual tuning will still be required.

—- END OF JEP —-

This section is only for documentation purposes.

Current Ergonomics

Currently, the default ergonomics of the HotSpot JVM are:

Memory Allocation

The default maximum heap size of the JVM varies from 50% to 25%, depending on how much memory is available in the environment. The table below describes the current heuristics of OpenJDK 21:

Max heap size

Available memory Default
< 256 MB 50%
256 - 512 MB ~126 MB
>= 512 MB 25%

The initial heap size also varies. The table below describes the current heuristics of OpenJDK 21:

Initial heap size

Available memory Default
<= 512 MB 8 MB
>= 512 MB 1.5625%-1.7%

Min heap size

Available memory Default
64 MB – 8192 MB 8 MB

We can observe that these amounts don’t adequately map to the intended resource plan of dedicated environments. The user may have already considered allocating, e.g., 4GB of memory to the environment and expect the JVM to use nearly 4GB of memory. In this example, the JVM will instead use only 1GB of memory by default (25%) for its heap, and the user will have to manually tune the JVM if they want to ensure larger heap.

Furthermore, it is likely that the JVM can reclaim heap memory later than it does in shared environments, as the JVM is the only process running on such environment. Knowing it is the only or primary process, this means that the JVM may set the initial heap size closer to the maximum heap size while having a suitable minimum heap size for other memory pools (e.g., native memory).

Garbage Collector

The default garbage collector selection happens only among two: Serial GC and G1 GC, based on the number of active processors seen by the JVM, and the amount of available memory, with a slightly different way of defining the GC thread pool:

GC selection

Memory Processors GC selected
>=1792 MB >1 G1
Otherwise   Serial

Which GC is used by the application will impact the amount of native memory consumed by the JVM, and the throughput and latency of the application.

GC threads

We document the current implementation detail of GC threads ergonomically configured by the JVM for reference.

GC # of CPUs ConcGCThreads ParallelGCThreads
Serial GC any 0 0
G1 GC 1-8 max((ParallelGCThreads+2)/4, 1) ceil(#CPUs)
G1 GC >8 max((ParallelGCThreads+2)/4, 1) 8 + (#CPUs-8) * (5/8)

Dependencies

List related issues, in no particular order:

  1. JDK-8329758: ZGC: Automatic Heap Sizing
  2. JDK-8261242: [Linux] OSContainer::is_containerized() returns true when run outside a container
  3. JDK-8302744: Refactor Hotspot container detection code
  4. JDK-8264482: container info misleads on non-container environment
  5. JDK-8285277: Improve container support for memory limits
  6. JDK-8292742: Remove container support method cpu_share()
  7. JDK-8284900: Check InitialHeapSize and container memory limits before startup
  8. JDK-8278492: Confusion about parameter -XX:MinRAMPercentage
  9. JDK-8287185: Consolidate HotSpot and JDK tests for CgroupSubsystemFactory
  10. JDK-8078703: Ensure that GC’s use of processor count is correct
  11. JDK-8286991: Hotspot container subsystem unaware of VM moving cgroups
  12. JDK-8286212: Cgroup v1 initialization causes NPE on some systems
  13. Memory Allocation Table

Server and Client Class Machine

The HotSpot JVM has a concept of Server and Client Class machine model. Should this concept be deprecated?