diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index e517e8f30a0..01901acc0a3 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -142,7 +142,9 @@ julong os::free_memory() { return Bsd::available_memory(); } -// available here means free +// Available here means free. Note that this number is of no much use. As an estimate +// for future memory pressure it is far too conservative, since MacOS will use a lot +// of unused memory for caches, and return it willingly in case of needs. julong os::Bsd::available_memory() { uint64_t available = physical_memory() >> 2; #ifdef __APPLE__ diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 69b1b0a8b97..c7f44c78fc5 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -2511,6 +2511,11 @@ WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) return os::physical_memory(); WB_END +// Available memory of the host machine (container-aware) +WB_ENTRY(jlong, WB_HostAvailableMemory(JNIEnv* env, jobject o)) + return os::available_memory(); +WB_END + // Physical swap of the host machine (including containers), Linux only. WB_ENTRY(jlong, WB_HostPhysicalSwap(JNIEnv* env, jobject o)) LINUX_ONLY(return (jlong)os::Linux::host_swap();) @@ -2980,6 +2985,7 @@ static JNINativeMethod methods[] = { (void*)&WB_ValidateCgroup }, {CC"hostPhysicalMemory", CC"()J", (void*)&WB_HostPhysicalMemory }, {CC"hostPhysicalSwap", CC"()J", (void*)&WB_HostPhysicalSwap }, + {CC"hostAvailableMemory", CC"()J", (void*)&WB_HostAvailableMemory }, {CC"hostCPUs", CC"()I", (void*)&WB_HostCPUs }, {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, {CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache }, diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index a31376f82f5..974f9b9fd38 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -91,12 +91,6 @@ gc/TestAllocHumongousFragment.java#adaptive 8298781 generic-all gc/TestAllocHumongousFragment.java#aggressive 8298781 generic-all gc/TestAllocHumongousFragment.java#g1 8298781 generic-all gc/TestAllocHumongousFragment.java#static 8298781 generic-all -gc/TestAlwaysPreTouchBehavior.java#ParallelCollector 8334513 generic-all -gc/TestAlwaysPreTouchBehavior.java#SerialCollector 8334513 generic-all -gc/TestAlwaysPreTouchBehavior.java#Shenandoah 8334513 generic-all -gc/TestAlwaysPreTouchBehavior.java#G1 8334513 generic-all -gc/TestAlwaysPreTouchBehavior.java#Z 8334513 generic-all -gc/TestAlwaysPreTouchBehavior.java#Epsilon 8334513 generic-all gc/shenandoah/oom/TestAllocOutOfMemory.java#large 8344312 linux-ppc64le gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all diff --git a/test/hotspot/jtreg/gc/TestAlwaysPreTouchBehavior.java b/test/hotspot/jtreg/gc/TestAlwaysPreTouchBehavior.java index 9f6c915d1c0..b8197f70384 100644 --- a/test/hotspot/jtreg/gc/TestAlwaysPreTouchBehavior.java +++ b/test/hotspot/jtreg/gc/TestAlwaysPreTouchBehavior.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, 2024, Alibaba Group Holding Limited. All rights reserved. - * Copyright (c) 2024, Red Hat Inc. + * Copyright (c) 2024, 2025, Red Hat Inc. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,23 +30,25 @@ package gc; * @summary tests AlwaysPreTouch * @requires vm.gc.Parallel * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx512m -Xms512m -XX:+UseParallelGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx256m -Xms256m -XX:+UseParallelGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ - /** +/** * @test id=SerialCollector * @summary tests AlwaysPreTouch * @requires vm.gc.Serial * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx512m -Xms512m -XX:+UseSerialGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx256m -Xms256m -XX:+UseSerialGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ /** @@ -53,11 +56,12 @@ package gc; * @summary tests AlwaysPreTouch * @requires vm.gc.Shenandoah * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx512m -Xms512m -XX:+UseShenandoahGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx256m -Xms256m -XX:+UseShenandoahGC -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ /** @@ -65,11 +69,12 @@ package gc; * @summary tests AlwaysPreTouch * @requires vm.gc.G1 * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx512m -Xms512m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx256m -Xms256m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ /** @@ -77,11 +82,12 @@ package gc; * @summary tests AlwaysPreTouch * @requires vm.gc.Z * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -Xmx512m -Xms512m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -Xmx256m -Xms256m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ /** @@ -89,30 +95,67 @@ package gc; * @summary tests AlwaysPreTouch * @requires vm.gc.Epsilon * @requires os.maxMemory > 2G - * @requires os.family != "aix" + * @requires !vm.debug + * @requires os.family == "linux" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xmx512m -Xms512m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xmx256m -Xms256m -XX:+AlwaysPreTouch gc.TestAlwaysPreTouchBehavior */ - import jdk.test.lib.Asserts; import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; public class TestAlwaysPreTouchBehavior { + // + // This test tests the ability of the JVM to pretouch its java heap for test purposes (AlwaysPreTouch). We start a + // JVM with -XX:+AlwaysPreTouch, then observe RSS and expect to see RSS covering the entirety of the java heap, + // since it should all be pre-touched now. + // + // This test is important (we had pretouching break before) but very shaky since RSS of the JVM process is subject to + // host machine conditions. If there is memory pressure, we may swap parts of the heap out after pretouching and + // before measuring RSS, thus tainting the result. + // + // This test attempts to minimize the risk of false positives stemming from memory pressure by: + // - specifying @requires os.maxMemory > 2G + // - checking if the memory still available on the host machine after starting the process is lower than a + // certain required threshold; if it is, we take this as a sign of memory pressure and disregard test errors. + // - we only run this test with release JVMs - debug JVMs have a higher and more unpredictable RSS footprint + // and therefore would require a larger heap for a clear difference between non-pretouch and pretouch; also, + // there should not be much difference between debug and release code. + // + // Obviously, all of this is not bulletproof and only useful on Linux: + // - On MacOS, os::available_memory() drastically underreports available memory, so this technique would almost + // always fail to function + // - On AIX, we dont have a way to measure rss yet. + // public static void main(String [] args) { - long rss = WhiteBox.getWhiteBox().rss(); - System.out.println("RSS: " + rss); - if (rss == 0) { - System.out.println("cannot get RSS, just skip"); - return; // Did not get available RSS, just ignore this test. - } - Runtime runtime = Runtime.getRuntime(); - long committedMemory = runtime.totalMemory(); - Asserts.assertGreaterThan(rss, committedMemory, "RSS of this process(" + rss + "b) should be bigger than or equal to committed heap mem(" + committedMemory + "b)"); - } -} + long rss = WhiteBox.getWhiteBox().rss(); + System.out.println("RSS: " + rss); + long available = WhiteBox.getWhiteBox().hostAvailableMemory(); + System.out.println("Host available memory: " + available); + long heapSize = 256 * 1024 * 1024; + + // On Linux, a JVM that runs with 256M pre-committed heap will use about 60MB (release JVM) RSS. Barring + // memory pressure that causes us to lose RSS, pretouching should increase RSS to >256MB. So there should be a + // clear distinction between non-pretouched and pretouched. + long minRequiredRss = heapSize; + + // The minimum required available memory size to count test errors as errors (to somewhat safely disregard + // outside memory pressure as the culprit). We are over-cautious and require at least 1G free. We rather err + // on the side of disregarding true errors than to produce false positives (if pretouching is broken, at least + // some of the runs of this test will run on beefy enough machines and show the test as failed). + long requiredAvailable = 1024 * 1024 * 1024; + if (rss == 0) { + throw new SkippedException("cannot get RSS?"); + } + if (available > requiredAvailable) { + Asserts.assertGreaterThan(rss, minRequiredRss, "RSS of this process(" + rss + "b) should be bigger " + + "than or equal to heap size(" + heapSize + "b) (available memory: " + available + ")"); + } + } +} diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 4747740274e..10fad866ca3 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -816,6 +816,7 @@ public class WhiteBox { String procSelfMountinfo); public native void printOsInfo(); public native long hostPhysicalMemory(); + public native long hostAvailableMemory(); public native long hostPhysicalSwap(); public native int hostCPUs();