diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java index 0d060be2e78..28f82308cbb 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java @@ -23,8 +23,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.util.Constants; -import org.elasticsearch.cli.ExitCodes; -import org.elasticsearch.cli.UserException; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.logging.Loggers; @@ -178,6 +176,7 @@ final class BootstrapCheck { checks.add(new MaxMapCountCheck()); } checks.add(new ClientJvmCheck()); + checks.add(new UseSerialGCCheck()); checks.add(new OnErrorCheck()); checks.add(new OnOutOfMemoryErrorCheck()); return Collections.unmodifiableList(checks); @@ -500,6 +499,38 @@ final class BootstrapCheck { } + /** + * Checks if the serial collector is in use. This collector is single-threaded and devastating + * for performance and should not be used for a server application like Elasticsearch. + */ + static class UseSerialGCCheck implements BootstrapCheck.Check { + + @Override + public boolean check() { + return getUseSerialGC().equals("true"); + } + + // visible for testing + String getUseSerialGC() { + return JvmInfo.jvmInfo().useSerialGC(); + } + + @Override + public String errorMessage() { + return String.format( + Locale.ROOT, + "JVM is using the serial collector but should not be for the best performance; " + + "either it's the default for the VM [%s] or -XX:+UseSerialGC was explicitly specified", + JvmInfo.jvmInfo().getVmName()); + } + + @Override + public boolean isSystemCheck() { + return false; + } + + } + abstract static class MightForkCheck implements BootstrapCheck.Check { @Override diff --git a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java index 36179fb06ea..e277faafd5f 100644 --- a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java +++ b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java @@ -103,6 +103,7 @@ public class JvmInfo implements Writeable, ToXContent { String onOutOfMemoryError = null; String useCompressedOops = "unknown"; String useG1GC = "unknown"; + String useSerialGC = "unknown"; long configuredInitialHeapSize = -1; long configuredMaxHeapSize = -1; try { @@ -148,6 +149,13 @@ public class JvmInfo implements Writeable, ToXContent { configuredMaxHeapSize = Long.parseLong((String) valueMethod.invoke(maxHeapSizeVmOptionObject)); } catch (Exception ignored) { } + + try { + Object useSerialGCVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseSerialGC"); + useSerialGC = (String) valueMethod.invoke(useSerialGCVmOptionObject); + } catch (Exception ignored) { + } + } catch (Exception ignored) { } @@ -155,7 +163,7 @@ public class JvmInfo implements Writeable, ToXContent { INSTANCE = new JvmInfo(pid, System.getProperty("java.version"), runtimeMXBean.getVmName(), runtimeMXBean.getVmVersion(), runtimeMXBean.getVmVendor(), runtimeMXBean.getStartTime(), configuredInitialHeapSize, configuredMaxHeapSize, mem, inputArguments, bootClassPath, classPath, systemProperties, gcCollectors, memoryPools, onError, onOutOfMemoryError, - useCompressedOops, useG1GC); + useCompressedOops, useG1GC, useSerialGC); } public static JvmInfo jvmInfo() { @@ -186,11 +194,12 @@ public class JvmInfo implements Writeable, ToXContent { private final String onOutOfMemoryError; private final String useCompressedOops; private final String useG1GC; + private final String useSerialGC; private JvmInfo(long pid, String version, String vmName, String vmVersion, String vmVendor, long startTime, long configuredInitialHeapSize, long configuredMaxHeapSize, Mem mem, String[] inputArguments, String bootClassPath, String classPath, Map systemProperties, String[] gcCollectors, String[] memoryPools, String onError, - String onOutOfMemoryError, String useCompressedOops, String useG1GC) { + String onOutOfMemoryError, String useCompressedOops, String useG1GC, String useSerialGC) { this.pid = pid; this.version = version; this.vmName = vmName; @@ -210,6 +219,7 @@ public class JvmInfo implements Writeable, ToXContent { this.onOutOfMemoryError = onOutOfMemoryError; this.useCompressedOops = useCompressedOops; this.useG1GC = useG1GC; + this.useSerialGC = useSerialGC; } public JvmInfo(StreamInput in) throws IOException { @@ -230,12 +240,13 @@ public class JvmInfo implements Writeable, ToXContent { gcCollectors = in.readStringArray(); memoryPools = in.readStringArray(); useCompressedOops = in.readString(); - //the following members are only used locally for boostrap checks, never serialized nor printed out + //the following members are only used locally for bootstrap checks, never serialized nor printed out this.configuredMaxHeapSize = -1; this.configuredInitialHeapSize = -1; this.onError = null; this.onOutOfMemoryError = null; this.useG1GC = "unknown"; + this.useSerialGC = "unknown"; } @Override @@ -415,6 +426,10 @@ public class JvmInfo implements Writeable, ToXContent { return this.useG1GC; } + public String useSerialGC() { + return this.useSerialGC; + } + public String[] getGcCollectors() { return gcCollectors; } diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java index f6faeedc429..c038525fb0e 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java @@ -24,6 +24,7 @@ import org.apache.lucene.util.Constants; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.node.NodeValidationException; import org.elasticsearch.test.ESTestCase; @@ -407,6 +408,27 @@ public class BootstrapCheckTests extends ESTestCase { BootstrapCheck.check(true, false, Collections.singletonList(check), "testClientJvmCheck"); } + public void testUseSerialGCCheck() throws NodeValidationException { + final AtomicReference useSerialGC = new AtomicReference<>("true"); + final BootstrapCheck.Check check = new BootstrapCheck.UseSerialGCCheck() { + @Override + String getUseSerialGC() { + return useSerialGC.get(); + } + }; + + final NodeValidationException e = expectThrows( + NodeValidationException.class, + () -> BootstrapCheck.check(true, false, Collections.singletonList(check), "testUseSerialGCCheck")); + assertThat( + e.getMessage(), + containsString("JVM is using the serial collector but should not be for the best performance; " + "" + + "either it's the default for the VM [" + JvmInfo.jvmInfo().getVmName() +"] or -XX:+UseSerialGC was explicitly specified")); + + useSerialGC.set("false"); + BootstrapCheck.check(true, false, Collections.singletonList(check), "testUseSerialGCCheck"); + } + public void testMightForkCheck() throws NodeValidationException { final AtomicBoolean isSeccompInstalled = new AtomicBoolean(); final AtomicBoolean mightFork = new AtomicBoolean(); diff --git a/docs/reference/setup/bootstrap-checks.asciidoc b/docs/reference/setup/bootstrap-checks.asciidoc index 47b51975ea5..0f88d03401c 100644 --- a/docs/reference/setup/bootstrap-checks.asciidoc +++ b/docs/reference/setup/bootstrap-checks.asciidoc @@ -127,6 +127,20 @@ systems and operating systems, the server VM is the default. Additionally, Elasticsearch is configured by default to force the server VM. +=== Use serial collector check + +There are various garbage collectors for the OpenJDK-derived JVMs targeting +different workloads. The serial collector in particular is best suited for +single logical CPU machines or extremely small heaps, neither of which are +suitable for running Elasticsearch. Using the serial collector with +Elasticsearch can be devastating for performance. The serial collector check +ensures that Elasticsearch is not configured to run with the serial +collector. To pass the serial collector check, you must not start Elasticsearch +with the serial collector (whether it's from the defaults for the JVM that +you're using, or you've explicitly specified it with `-XX:+UseSerialGC`). Note +that the default JVM configuration that ship with Elasticsearch configures +Elasticsearch to use the CMS collector. + === OnError and OnOutOfMemoryError checks The JVM options `OnError` and `OnOutOfMemoryError` enable executing