From 4f5d73bcb7d654f327b01ac4f28b36da83fbc61a Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 13 Apr 2016 09:45:30 -0400 Subject: [PATCH] Add heap size bootstrap check This commit adds a bootstrap check to ensure that the initial heap size and max heap size are set equal to each other. --- .../bootstrap/BootstrapCheck.java | 34 +++++++++++++ .../elasticsearch/monitor/jvm/JvmInfo.java | 49 +++++++++++++++---- .../bootstrap/BootstrapCheckTests.java | 43 ++++++++++++++++ 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java index 85cc6497261..a49a1492d29 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.discovery.zen.elect.ElectMasterService; +import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.monitor.process.ProcessProbe; import org.elasticsearch.node.Node; @@ -108,6 +109,7 @@ final class BootstrapCheck { // the list of checks to execute static List checks(final Settings settings) { final List checks = new ArrayList<>(); + checks.add(new HeapSizeCheck()); final FileDescriptorCheck fileDescriptorCheck = Constants.MAC_OS_X ? new OsXFileDescriptorCheck() : new FileDescriptorCheck(); checks.add(fileDescriptorCheck); @@ -143,6 +145,38 @@ final class BootstrapCheck { } + static class HeapSizeCheck implements BootstrapCheck.Check { + + @Override + public boolean check() { + final long initialHeapSize = getInitialHeapSize(); + final long maxHeapSize = getMaxHeapSize(); + return initialHeapSize != 0 && maxHeapSize != 0 && initialHeapSize != maxHeapSize; + } + + @Override + public String errorMessage() { + return String.format( + Locale.ROOT, + "initial heap size [%d] not equal to maximum heap size [%d]; " + + "this can cause resize pauses and prevents mlockall from locking the entire heap", + getInitialHeapSize(), + getMaxHeapSize() + ); + } + + // visible for testing + long getInitialHeapSize() { + return JvmInfo.jvmInfo().getConfiguredInitialHeapSize(); + } + + // visible for testing + long getMaxHeapSize() { + return JvmInfo.jvmInfo().getConfiguredMaxHeapSize(); + } + + } + static class OsXFileDescriptorCheck extends FileDescriptorCheck { public OsXFileDescriptorCheck() { 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 afe1cc13eb2..d35497834f7 100644 --- a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java +++ b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java @@ -114,15 +114,33 @@ public class JvmInfo implements Streamable, ToXContent { Class vmOptionClazz = Class.forName("com.sun.management.VMOption"); PlatformManagedObject hotSpotDiagnosticMXBean = ManagementFactory.getPlatformMXBean(clazz); Method vmOptionMethod = clazz.getMethod("getVMOption", String.class); - Object useCompressedOopsVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseCompressedOops"); Method valueMethod = vmOptionClazz.getMethod("getValue"); - info.useCompressedOops = (String)valueMethod.invoke(useCompressedOopsVmOption); - Object useG1GCVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseG1GC"); - info.useG1GC = (String)valueMethod.invoke(useG1GCVmOption); - } catch (Throwable t) { - // unable to deduce the state of compressed oops - info.useCompressedOops = "unknown"; - info.useG1GC = "unknown"; + + try { + Object useCompressedOopsVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseCompressedOops"); + info.useCompressedOops = (String) valueMethod.invoke(useCompressedOopsVmOption); + } catch (Exception ignored) { + } + + try { + Object useG1GCVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseG1GC"); + info.useG1GC = (String) valueMethod.invoke(useG1GCVmOption); + } catch (Exception ignored) { + } + + try { + Object initialHeapSizeVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "InitialHeapSize"); + info.configuredInitialHeapSize = Long.parseLong((String) valueMethod.invoke(initialHeapSizeVmOption)); + } catch (Exception ignored) { + } + + try { + Object maxHeapSizeVmOption = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "MaxHeapSize"); + info.configuredMaxHeapSize = Long.parseLong((String) valueMethod.invoke(maxHeapSizeVmOption)); + } catch (Exception ignored) { + } + } catch (Exception ignored) { + } INSTANCE = info; @@ -146,6 +164,9 @@ public class JvmInfo implements Streamable, ToXContent { long startTime = -1; + private long configuredInitialHeapSize; + private long configuredMaxHeapSize; + Mem mem; String[] inputArguments; @@ -159,9 +180,9 @@ public class JvmInfo implements Streamable, ToXContent { String[] gcCollectors = Strings.EMPTY_ARRAY; String[] memoryPools = Strings.EMPTY_ARRAY; - private String useCompressedOops; + private String useCompressedOops = "unknown"; - private String useG1GC; + private String useG1GC = "unknown"; private JvmInfo() { } @@ -286,6 +307,14 @@ public class JvmInfo implements Streamable, ToXContent { return this.systemProperties; } + public long getConfiguredInitialHeapSize() { + return configuredInitialHeapSize; + } + + public long getConfiguredMaxHeapSize() { + return configuredMaxHeapSize; + } + /** * The value of the JVM flag UseCompressedOops, if available otherwise * "unknown". The value "unknown" indicates that an attempt was diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java index 4f2c6f67bf2..fcbac5b72fa 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java @@ -137,6 +137,49 @@ public class BootstrapCheckTests extends ESTestCase { assertThat(suppressed[1], hasToString(containsString("second"))); } + public void testHeapSizeCheck() { + final int initial = randomIntBetween(0, Integer.MAX_VALUE - 1); + final int max = randomIntBetween(initial + 1, Integer.MAX_VALUE); + final AtomicLong initialHeapSize = new AtomicLong(initial); + final AtomicLong maxHeapSize = new AtomicLong(max); + + final BootstrapCheck.HeapSizeCheck check = new BootstrapCheck.HeapSizeCheck() { + @Override + long getInitialHeapSize() { + return initialHeapSize.get(); + } + + @Override + long getMaxHeapSize() { + return maxHeapSize.get(); + } + }; + + try { + BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck"); + fail("should have failed to initial heap size not equal to max heap size"); + } catch (final RuntimeException e) { + assertThat( + e.getMessage(), + containsString("initial heap size [" + initialHeapSize.get() + "] " + + "not equal to maximum heap size [" + maxHeapSize.get() + "]") + ); + } + + initialHeapSize.set(maxHeapSize.get()); + + BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck"); + + // nothing should happen if the initial heap size or the max + // heap size is not available + if (randomBoolean()) { + initialHeapSize.set(0); + } else { + maxHeapSize.set(0); + } + BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck"); + } + public void testFileDescriptorLimits() { final boolean osX = randomBoolean(); // simulates OS X versus non-OS X final int limit = osX ? 10240 : 1 << 16;