Skip to content

Commit dde4695

Browse files
authored
Use different G1GC options for small heaps (#59667) (#63413)
Our benchmarks have demonstrated that Elasticsearch performs better with `-XX:G1HeapRegionSize=4M` and `-XX:G1ReservePercent=15` options for the small heap sizes. With this commit we ergonomically choose different G1GC options for heap sizes smaller than (not including) 8GB. Co-authored-by: Daniel Mitterdorfer [email protected]
1 parent 7a4f1b6 commit dde4695

File tree

3 files changed

+132
-11
lines changed

3 files changed

+132
-11
lines changed

distribution/src/config/jvm.options

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@
4444
# 10-13:-XX:-UseConcMarkSweepGC
4545
# 10-13:-XX:-UseCMSInitiatingOccupancyOnly
4646
14-:-XX:+UseG1GC
47-
14-:-XX:G1ReservePercent=25
48-
14-:-XX:InitiatingHeapOccupancyPercent=30
4947

5048
## JVM temporary directory
5149
-Djava.io.tmpdir=${ES_TMPDIR}

distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmErgonomics.java

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private JvmErgonomics() {
5656
*/
5757
static List<String> choose(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
5858
final List<String> ergonomicChoices = new ArrayList<>();
59-
final Map<String, Optional<String>> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
59+
final Map<String, JvmOption> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
6060
final long heapSize = extractHeapSize(finalJvmOptions);
6161
final long maxDirectMemorySize = extractMaxDirectMemorySize(finalJvmOptions);
6262

@@ -75,20 +75,59 @@ static List<String> choose(final List<String> userDefinedJvmOptions) throws Inte
7575

7676
ergonomicChoices.add("-XX:MaxDirectMemorySize=" + heapSize / 2);
7777
}
78+
// Elasticsearch supports G1GC on JDK10+
79+
if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 10) {
80+
final boolean tuneG1GCForSmallHeap = tuneG1GCForSmallHeap(heapSize);
81+
final boolean tuneG1GCHeapRegion = tuneG1GCHeapRegion(finalJvmOptions, tuneG1GCForSmallHeap);
82+
final boolean tuneG1GCInitiatingHeapOccupancyPercent = tuneG1GCInitiatingHeapOccupancyPercent(finalJvmOptions);
83+
final int tuneG1GCReservePercent = tuneG1GCReservePercent(finalJvmOptions, tuneG1GCForSmallHeap);
84+
85+
if (tuneG1GCHeapRegion) {
86+
ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
87+
}
88+
if (tuneG1GCInitiatingHeapOccupancyPercent) {
89+
ergonomicChoices.add("-XX:InitiatingHeapOccupancyPercent=30");
90+
}
91+
if (tuneG1GCReservePercent != 0) {
92+
ergonomicChoices.add("-XX:G1ReservePercent=" + tuneG1GCReservePercent);
93+
}
94+
}
95+
7896
return ergonomicChoices;
7997
}
8098

8199
private static final Pattern OPTION = Pattern.compile(
82-
"^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}(\\s+\\{[^}]+})?"
100+
"^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}(\\s+\\{(?<origin>[^}]+)})?"
83101
);
84102

85-
static Map<String, Optional<String>> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException,
86-
IOException {
103+
private static class JvmOption {
104+
private final String value;
105+
private final String origin;
106+
107+
JvmOption(String value, String origin) {
108+
this.value = value;
109+
this.origin = origin;
110+
}
111+
112+
public Optional<String> getValue() {
113+
return Optional.ofNullable(value);
114+
}
115+
116+
public String getMandatoryValue() {
117+
return value;
118+
}
119+
120+
public boolean isCommandLineOrigin() {
121+
return "command line".equals(this.origin);
122+
}
123+
}
124+
125+
static Map<String, JvmOption> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
87126
return Collections.unmodifiableMap(
88127
flagsFinal(userDefinedJvmOptions).stream()
89128
.map(OPTION::matcher)
90129
.filter(Matcher::matches)
91-
.collect(Collectors.toMap(m -> m.group("flag"), m -> Optional.ofNullable(m.group("value"))))
130+
.collect(Collectors.toMap(m -> m.group("flag"), m -> new JvmOption(m.group("value"), m.group("origin"))))
92131
);
93132
}
94133

@@ -136,12 +175,42 @@ private static List<String> readLinesFromInputStream(final InputStream is) throw
136175
}
137176

138177
// package private for testing
139-
static Long extractHeapSize(final Map<String, Optional<String>> finalJvmOptions) {
140-
return Long.parseLong(finalJvmOptions.get("MaxHeapSize").get());
178+
static Long extractHeapSize(final Map<String, JvmOption> finalJvmOptions) {
179+
return Long.parseLong(finalJvmOptions.get("MaxHeapSize").getMandatoryValue());
180+
}
181+
182+
static long extractMaxDirectMemorySize(final Map<String, JvmOption> finalJvmOptions) {
183+
return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").getMandatoryValue());
184+
}
185+
186+
// Tune G1GC options for heaps < 8GB
187+
static boolean tuneG1GCForSmallHeap(final long heapSize) {
188+
return heapSize < 8L << 30;
189+
}
190+
191+
static boolean tuneG1GCHeapRegion(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
192+
JvmOption g1GCHeapRegion = finalJvmOptions.get("G1HeapRegionSize");
193+
JvmOption g1GC = finalJvmOptions.get("UseG1GC");
194+
return (tuneG1GCForSmallHeap && g1GC.getMandatoryValue().equals("true") && g1GCHeapRegion.isCommandLineOrigin() == false);
195+
}
196+
197+
static int tuneG1GCReservePercent(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
198+
JvmOption g1GC = finalJvmOptions.get("UseG1GC");
199+
JvmOption g1GCReservePercent = finalJvmOptions.get("G1ReservePercent");
200+
if (g1GC.getMandatoryValue().equals("true")) {
201+
if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap) {
202+
return 15;
203+
} else if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap == false) {
204+
return 25;
205+
}
206+
}
207+
return 0;
141208
}
142209

143-
static long extractMaxDirectMemorySize(final Map<String, Optional<String>> finalJvmOptions) {
144-
return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").get());
210+
static boolean tuneG1GCInitiatingHeapOccupancyPercent(final Map<String, JvmOption> finalJvmOptions) {
211+
JvmOption g1GC = finalJvmOptions.get("UseG1GC");
212+
JvmOption g1GCInitiatingHeapOccupancyPercent = finalJvmOptions.get("InitiatingHeapOccupancyPercent");
213+
return g1GCInitiatingHeapOccupancyPercent.isCommandLineOrigin() == false && g1GC.getMandatoryValue().equals("true");
145214
}
146215

147216
private static final Pattern SYSTEM_PROPERTY = Pattern.compile("^-D(?<key>[\\w+].*?)=(?<value>.*)$");

distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmErgonomicsTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,60 @@ public void testExtractSystemProperties() {
122122
assertEquals(expectedSystemProperties, parsedSystemProperties);
123123
}
124124

125+
public void testG1GOptionsForSmallHeap() throws InterruptedException, IOException {
126+
List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC"));
127+
if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 10) {
128+
assertThat(jvmErgonomics, hasItem("-XX:G1HeapRegionSize=4m"));
129+
assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30"));
130+
assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15"));
131+
} else {
132+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
133+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
134+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
135+
}
136+
}
137+
138+
public void testG1GOptionsForSmallHeapWhenTuningSet() throws InterruptedException, IOException {
139+
List<String> jvmErgonomics = JvmErgonomics.choose(
140+
Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC", "-XX:InitiatingHeapOccupancyPercent=45")
141+
);
142+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
143+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
144+
if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 10) {
145+
assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15"));
146+
} else {
147+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
148+
}
149+
}
150+
151+
public void testG1GOptionsForLargeHeap() throws InterruptedException, IOException {
152+
List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC"));
153+
if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 10) {
154+
assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30"));
155+
assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=25"));
156+
} else {
157+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
158+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
159+
}
160+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
161+
}
162+
163+
public void testG1GOptionsForSmallHeapWhenOtherGCSet() throws InterruptedException, IOException {
164+
List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseParallelGC"));
165+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
166+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
167+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
168+
}
169+
170+
public void testG1GOptionsForLargeHeapWhenTuningSet() throws InterruptedException, IOException {
171+
List<String> jvmErgonomics = JvmErgonomics.choose(
172+
Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC", "-XX:InitiatingHeapOccupancyPercent=60", "-XX:G1ReservePercent=10")
173+
);
174+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
175+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
176+
assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
177+
}
178+
125179
public void testExtractNoSystemProperties() {
126180
Map<String, String> parsedSystemProperties = JvmErgonomics.extractSystemProperties(Arrays.asList("-Xms1024M", "-Xmx1024M"));
127181
assertTrue(parsedSystemProperties.isEmpty());

0 commit comments

Comments
 (0)