diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java index 8f0ac0102b..8797711bf4 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java @@ -122,9 +122,11 @@ public long getMemoryFailCount() { @Override public long getMemoryLimit() { long subsMem = subsystem.getMemoryLimit(); + long systemTotal = getTotalMemorySize0(); + assert(systemTotal > 0); // Catch the cgroup memory limit exceeding host physical memory. // Treat this as unlimited. - if (subsMem >= getTotalMemorySize0()) { + if (subsMem >= systemTotal) { return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } return subsMem; @@ -142,7 +144,15 @@ public long getTcpMemoryUsage() { @Override public long getMemoryAndSwapLimit() { - return subsystem.getMemoryAndSwapLimit(); + long totalSystemMemSwap = getTotalMemorySize0() + getTotalSwapSize0(); + assert(totalSystemMemSwap > 0); + // Catch the cgroup memory and swap limit exceeding host physical swap + // and memory. Treat this case as unlimited. + long subsSwapMem = subsystem.getMemoryAndSwapLimit(); + if (subsSwapMem >= totalSystemMemSwap) { + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; + } + return subsSwapMem; } @Override @@ -185,5 +195,6 @@ public static Metrics getInstance() { private static native boolean isUseContainerSupport(); private static native long getTotalMemorySize0(); + private static native long getTotalSwapSize0(); } diff --git a/src/java.base/linux/native/libjava/CgroupMetrics.c b/src/java.base/linux/native/libjava/CgroupMetrics.c index d9bb7369f1..4f7847edbd 100644 --- a/src/java.base/linux/native/libjava/CgroupMetrics.c +++ b/src/java.base/linux/native/libjava/CgroupMetrics.c @@ -23,6 +23,7 @@ * questions. */ #include +#include #include "jni.h" #include "jvm.h" @@ -43,3 +44,15 @@ Java_jdk_internal_platform_CgroupMetrics_getTotalMemorySize0 jlong page_size = sysconf(_SC_PAGESIZE); return pages * page_size; } + +JNIEXPORT jlong JNICALL +Java_jdk_internal_platform_CgroupMetrics_getTotalSwapSize0 + (JNIEnv *env, jclass ignored) +{ + struct sysinfo si; + int retval = sysinfo(&si); + if (retval < 0) { + return 0; // syinfo failed, treat as no swap + } + return (jlong)si.totalswap; +} diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java index ae364bb37b..a8e67576d2 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java @@ -37,6 +37,7 @@ * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:whitebox.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestMemoryAwareness */ +import java.util.function.Consumer; import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; @@ -96,7 +97,9 @@ public static void main(String[] args) throws Exception { true /* additional cgroup fs mounts */ ); testOSMXBeanIgnoresMemLimitExceedingPhysicalMemory(); + testOSMXBeanIgnoresSwapLimitExceedingPhysical(); testMetricsExceedingPhysicalMemory(); + testMetricsSwapExceedingPhysical(); testContainerMemExceedsPhysical(); } finally { if (!DockerTestUtils.RETAIN_IMAGE_AFTER_TEST) { @@ -183,6 +186,13 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation, private static void testOperatingSystemMXBeanAwareness(String memoryAllocation, String expectedMemory, String swapAllocation, String expectedSwap, boolean addCgroupMounts) throws Exception { + Consumer noOp = o -> {}; + testOperatingSystemMXBeanAwareness(memoryAllocation, expectedMemory, swapAllocation, expectedSwap, false, noOp); + } + + private static void testOperatingSystemMXBeanAwareness(String memoryAllocation, String expectedMemory, + String swapAllocation, String expectedSwap, boolean addCgroupMounts, + Consumer additionalMatch) throws Exception { Common.logNewTestCase("Check OperatingSystemMXBean"); @@ -191,6 +201,7 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation, "--memory", memoryAllocation, "--memory-swap", swapAllocation ) + .addJavaOpts("-esa") // CheckOperatingSystemMXBean uses Metrics (jdk.internal.platform) for // diagnostics .addJavaOpts("--add-exports") @@ -224,9 +235,9 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation, } catch(RuntimeException ex) { out.shouldMatch("OperatingSystemMXBean\\.getFreeSwapSpaceSize: 0"); } + additionalMatch.accept(out); } - // JDK-8292541: Ensure OperatingSystemMXBean ignores container memory limits above the host's physical memory. private static void testOSMXBeanIgnoresMemLimitExceedingPhysicalMemory() throws Exception { @@ -235,6 +246,35 @@ private static void testOSMXBeanIgnoresMemLimitExceedingPhysicalMemory() testOperatingSystemMXBeanAwareness(badMem, hostMaxMem, badMem, hostMaxMem); } + private static void testOSMXBeanIgnoresSwapLimitExceedingPhysical() + throws Exception { + long totalSwap = wb.hostPhysicalSwap() + wb.hostPhysicalMemory(); + String expectedSwap = Long.valueOf(totalSwap).toString(); + String hostMaxMem = getHostMaxMemory(); + String badMem = hostMaxMem + "0"; + final String badSwap = expectedSwap + "0"; + testOperatingSystemMXBeanAwareness(badMem, hostMaxMem, badSwap, expectedSwap, false, o -> { + o.shouldNotContain("Metrics.getMemoryAndSwapLimit() == " + badSwap); + }); + } + + private static void testMetricsSwapExceedingPhysical() + throws Exception { + Common.logNewTestCase("Metrics ignore container swap memory limit exceeding physical"); + long totalSwap = wb.hostPhysicalSwap() + wb.hostPhysicalMemory(); + String expectedSwap = Long.valueOf(totalSwap).toString(); + final String badSwap = expectedSwap + "0"; + String badMem = getHostMaxMemory() + "0"; + DockerRunOptions opts = Common.newOpts(imageName) + .addJavaOpts("-XshowSettings:system") + .addDockerOpts("--memory", badMem) + .addDockerOpts("--memory-swap", badSwap); + + OutputAnalyzer out = DockerTestUtils.dockerRunJava(opts); + out.shouldContain("Memory Limit: Unlimited"); + out.shouldContain("Memory & Swap Limit: Unlimited"); + } + // JDK-8292541: Ensure Metrics ignores container memory limits above the host's physical memory. private static void testMetricsExceedingPhysicalMemory() throws Exception {