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.
This commit is contained in:
Jason Tedor 2016-04-13 09:45:30 -04:00
parent 5ca4304b23
commit 4f5d73bcb7
3 changed files with 116 additions and 10 deletions

View File

@ -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<Check> checks(final Settings settings) {
final List<Check> 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() {

View File

@ -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");
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 (Throwable t) {
// unable to deduce the state of compressed oops
info.useCompressedOops = "unknown";
info.useG1GC = "unknown";
} 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

View File

@ -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;