28bba54c创建于 4月22日历史提交
From 4865d58b6f87a2525722e7a903a27abb196d597a Mon Sep 17 00:00:00 2001
From: XingyuWu <wuxingyu13@huawei.com>
Date: Sat, 28 Mar 2026 09:47:48 +0800
Subject: [PATCH] Enhance NUMANodesRandom

---
 src/hotspot/cpu/aarch64/globals_aarch64.hpp   |  11 +-
 .../os_cpu/linux_aarch64/os_linux_aarch64.cpp | 179 ++++++++++++++++--
 src/hotspot/share/runtime/arguments.cpp       |  11 ++
 .../hotspot/jtreg/containers/docker/Args.java |  31 +++
 .../containers/docker/TestNUMABindPolicy.java | 102 ++++++++++
 .../docker/TestNUMANodesRandom.java           |   4 +-
 .../TestTargetSurvivorRatioFlag.java          |  16 ++
 7 files changed, 329 insertions(+), 25 deletions(-)
 create mode 100644 test/hotspot/jtreg/containers/docker/Args.java
 create mode 100644 test/hotspot/jtreg/containers/docker/TestNUMABindPolicy.java

diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
index 9a834a779..39ceb46ea 100644
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -207,9 +207,16 @@ define_pd_global(intx, InlineSmallCode,          1000);
           "<nodes> can be '0-2', '0,1,2', 'all' and so on.")            \
                                                                         \
   product(uintx, NUMANodesRandom, 0,                                    \
-          "Number of continuous nodes to bind"                          \
+          "Number of continuous nodes to bind to cpu"                   \
           "with the first node randomly chosen."                        \
-          "NUMANodesRandom has higher priority than NUMANodes")         \
+          "If NUMANodes is set, NUMANodesRandom selects nodes randomly" \
+          "within this range.")                                         \
+  product(intx, NUMAMemNodesRandom, 0,                                  \
+          "Number of continuous nodes to bind to memory"                \
+          "with the first N nodes chosen by NUMANodesRandom.")          \
+  product(ccstr, NUMABindPolicy, NULL,                                  \
+          "Enable deterministic NUMA placement with combined Options,"  \
+          "including prefix=<id> and div=<N>.")                         \
 
 // end of ARCH_FLAGS
 
diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
index 206f4d652..b882d0c0f 100644
--- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
+++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
@@ -392,6 +392,63 @@ static inline void atomic_copy64(const volatile void *src, volatile void *dst) {
 }
 extern char** argv_for_execvp;
 
+static bool is_non_negative_integer(const char* buf, int* res) {
+  julong v;
+  if (!Arguments::atojulong(buf, &v)) {
+    return false;
+  }
+  if (v > INT_MAX) {
+    return false;
+  }
+  *res = (int)v;
+  return true;
+}
+
+static bool parse_bind_policy(const char* policy, const char* &prefix, int &div) {
+  const char* p = policy;
+  while (*p != '\0') {
+    const char* eq = strchr(p, '=');
+    if (!eq) break;
+
+    const char* key = p;
+    int key_len = eq - key;
+    const char* val = eq + 1;
+
+    const char* comma = strchr(val, ',');
+    int val_len = comma ? (comma - val) : (int)strlen(val);
+
+    if (key_len == 6 && strncmp(key, "prefix", 6) == 0) {
+      if (val_len == 0) {
+        if (LogNUMANodes) {
+          warning("NUMABindPolicy: prefix cannot be empty.");
+        }
+        return false;
+      }
+      char* tmp = NEW_C_HEAP_ARRAY(char, val_len + 1, mtInternal);
+      memcpy(tmp, val, val_len);
+      tmp[val_len] = '\0';
+      prefix = tmp;
+    } else if (key_len == 3 && strncmp(key, "div", 3) == 0) {
+      if (!is_non_negative_integer(val, &div) || div == 0) {
+        if (LogNUMANodes) {
+          warning("NUMABindPolicy: div must be a positive integer, got '%s'", val);
+        }
+        return false;
+      }
+    }
+
+    if (!comma) break;
+    p = comma + 1;
+  }
+  if (prefix == NULL || div == 0) {
+    if (LogNUMANodes) {
+      warning("Lack of prefix/div. Feature disabled.");
+    }
+    return false;
+  }
+  return true;
+}
+
 void os::Linux::chose_numa_nodes() {
   const char* numa_chosen_env = getenv("_JVM_NUMA_BINDING_DONE");
   if (numa_chosen_env != NULL && strcmp(numa_chosen_env, "1") == 0) {
@@ -402,6 +459,9 @@ void os::Linux::chose_numa_nodes() {
   }
 
   if (NUMANodes == NULL && NUMANodesRandom == 0) {
+    if (LogNUMANodes) {
+      warning("Numa binding will not work without NUMANodes or NUMANodesRandom.");
+    }
     return;
   }
 
@@ -410,7 +470,9 @@ void os::Linux::chose_numa_nodes() {
   int nodes_num = Linux::numa_max_node() + 1;
   const int MAXNODE = 100;
   if (nodes_num <= 0 || nodes_num >= MAXNODE) {
-    warning("Invalid NUMA nodes number: %d", nodes_num);
+    if (LogNUMANodes) {
+      warning("Invalid NUMA nodes number: %d", nodes_num);
+    }
     return;
   }
 
@@ -525,11 +587,17 @@ void os::Linux::chose_numa_nodes() {
   }
 
   // Determine the number of nodes to be selected
-  int nodes_to_select = NUMANodesRandom;
+  int nodes_to_select_cpu = NUMANodesRandom;
+  int nodes_to_select_mem = NUMAMemNodesRandom;
 
   // If NUMANodesRandom is not set or the value is invalid, use all available nodes
-  if (nodes_to_select <= 0 || nodes_to_select > available_nodes_count) {
-    nodes_to_select = available_nodes_count;
+  if (nodes_to_select_cpu <= 0 || nodes_to_select_cpu > available_nodes_count) {
+    nodes_to_select_cpu = available_nodes_count;
+  }
+
+  // If NUMAMemNodesRandom is invalid, use the same nodes as cpu.
+  if (nodes_to_select_mem < nodes_to_select_cpu || nodes_to_select_mem > available_nodes_count) {
+    nodes_to_select_mem = nodes_to_select_cpu;
   }
 
   if (LogNUMANodes) {
@@ -537,13 +605,67 @@ void os::Linux::chose_numa_nodes() {
       warning("NUMANodes filter applied: %s, available nodes after filter: %d",
               NUMANodes, available_nodes_count);
     }
-    warning("NUMANodesRandom=%lu, will select %d nodes from %d available nodes",
-            NUMANodesRandom, nodes_to_select, available_nodes_count);
+    warning("NUMANodesRandom=%lu, will select %d nodes from %d available nodes for cpu",
+            NUMANodesRandom, nodes_to_select_cpu, available_nodes_count);
+    warning("NUMAMemNodesRandom=%lu, will select %d nodes from %d available nodes for memory",
+            NUMAMemNodesRandom, nodes_to_select_mem, available_nodes_count);
+  }
+
+  const char* policy = NUMABindPolicy;
+  const char* process_prefix = NULL;
+  int process_div = 0;
+
+  int random_number;
+  if (policy != NULL) {
+    if (!parse_bind_policy(policy, process_prefix, process_div)) { 
+      return;
+    }
+
+    if (LogNUMANodes) {
+      warning("NUMABindPolicy: prefix=%s div=%d",
+              process_prefix != NULL ? process_prefix : "<null>",
+              process_div);
+    }
+
+    int i = 0;
+    int prefix_id = -1;
+    while (argv_for_execvp[i] != NULL) {
+      const char* arg = argv_for_execvp[i];
+      if (strcmp(arg, process_prefix) == 0 && argv_for_execvp[i + 1] != NULL) {
+        if (!is_non_negative_integer(argv_for_execvp[i + 1], &prefix_id)) {
+          if (LogNUMANodes) {
+            warning("Invalid prefix id, feature disabled.");
+          }
+          return;
+        }
+        break;
+      }
+      int key_len = strlen(process_prefix);
+      if (strncmp(arg, process_prefix, key_len) == 0 && arg[key_len] == '=') {
+        const char* val = arg + key_len + 1;
+        if (*val == '\0' || !is_non_negative_integer(val, &prefix_id)) {
+          if (LogNUMANodes) {
+            warning("Invalid prefix id, feature disabled.");
+          }
+          return;
+        }
+        break;
+      }
+      i++;
+    }
+    if (prefix_id < 0) {
+      if (LogNUMANodes) {
+        warning("Cannot find corresponding process according to prefix. Feature disabled.");
+      }
+      return;
+    }
+    random_number = prefix_id / process_div;
+  } else {
+    // Use PID as the random seed
+    random_number = getpid();
   }
 
-  // Use PID as the random seed
-  int pid = getpid();
-  int start_index = pid % available_nodes_count;
+  int start_index = random_number % available_nodes_count;
   int start_node = available_nodes[start_index];
 
   // Check whether the starting node can be bound to memory
@@ -572,14 +694,14 @@ void os::Linux::chose_numa_nodes() {
     warning("Start node: %d", start_node);
   }
 
-  // Select nodes: Choose the nearest nodes_to_select node based on distance
+  // Select nodes: Choose the nearest nodes_to_select_cpu/nodes_to_select_mem node based on distance
   int selected_nodes[MAXNODE];
   int selected_count = 0;
 
   // First node is the starting node
   selected_nodes[selected_count++] = start_node;
 
-  if (nodes_to_select == 1) {
+  if (nodes_to_select_mem == 1) {
     if (LogNUMANodes) {
       warning("Selected node %d", start_node);
     }
@@ -589,7 +711,7 @@ void os::Linux::chose_numa_nodes() {
     node_selected[start_node] = true;
 
     // Select the nearest nodes
-    while (selected_count < nodes_to_select) {
+    while (selected_count < nodes_to_select_mem) {
       int nearest_node = -1;
       int min_total_distance = MAX_DISTANCE;
 
@@ -666,7 +788,7 @@ void os::Linux::chose_numa_nodes() {
   }
 
   // Only retain the CPU on the selected node
-  for (int i = 0; i < selected_count; i++) {
+  for (int i = 0; i < MIN2(selected_count, nodes_to_select_cpu); i++) {
     int node_id = selected_nodes[i];
 
     if (_numa_node_to_cpus_v2(node_id, node_cpumask) != 0) {
@@ -693,14 +815,24 @@ void os::Linux::chose_numa_nodes() {
   os::Linux::numa_bitmask_free(node_cpumask);
 
   // Set the bit mask
-  char buf[256] = {0};
-  int buf_pos = 0;
+  char buf_cpu[256] = {0};
+  int pos_cpu = 0;
 
-  for (int i = 0; i < selected_count; i++) {
+  for (int i = 0; i < MIN2(selected_count, nodes_to_select_cpu); i++) {
     if (i > 0) {
-      buf_pos += snprintf(buf + buf_pos, sizeof(buf) - buf_pos, ",");
+      pos_cpu += snprintf(buf_cpu + pos_cpu, sizeof(buf_cpu) - pos_cpu, ",");
     }
-    buf_pos += snprintf(buf + buf_pos, sizeof(buf) - buf_pos, "%d", selected_nodes[i]);
+    pos_cpu += snprintf(buf_cpu + pos_cpu, sizeof(buf_cpu) - pos_cpu, "%d", selected_nodes[i]);
+  }
+
+  char buf_mem[256] = {0};
+  int pos_mem = 0;
+
+  for (int i = 0; i < selected_count; i++) {   // mem uses ALL selected nodes
+    if (i > 0) {
+      pos_mem += snprintf(buf_mem + pos_mem, sizeof(buf_mem) - pos_mem, ",");
+    }
+    pos_mem += snprintf(buf_mem + pos_mem, sizeof(buf_mem) - pos_mem, "%d", selected_nodes[i]);
   }
 
   bitmask* mask = numa_allocate_nodemask();
@@ -709,10 +841,11 @@ void os::Linux::chose_numa_nodes() {
     numa_bitmask_setbit(mask, selected_nodes[i]);  // Set the bit directly
   }
 
-  if (os::Linux::numa_bitmask_equal(mask, os::Linux::_numa_membind_bitmask)) {
+  if (os::Linux::numa_bitmask_equal(mask, os::Linux::_numa_membind_bitmask) &&
+      CPU_EQUAL(&new_cpu_mask, &original_cpu_mask)) {
     os::Linux::numa_bitmask_free(mask);
     if (LogNUMANodes) {
-      warning("Mempolicy is not changed, param: %s", buf);
+      warning("CpuPolicy and Mempolicy is not changed, cpu param: %s, mem param: %s", buf_cpu, buf_mem);
     }
     return;
   }
@@ -732,6 +865,10 @@ void os::Linux::chose_numa_nodes() {
     perror("numa_set_membind");
   }
 
+  if (LogNUMANodes) {
+    warning("Successfully bound mem to %d node(s): %s", selected_count, buf_mem);
+  }
+
   // New CPU affinity
   if (sched_setaffinity(0, sizeof(cpu_set_t), &new_cpu_mask) == -1) {
     perror("sched_setaffinity");
@@ -742,7 +879,7 @@ void os::Linux::chose_numa_nodes() {
   }
 
   if (LogNUMANodes) {
-    warning("Successfully bound to %d node(s): %s", selected_count, buf);
+    warning("Successfully bound cpu to %d node(s): %s", MIN2(selected_count, nodes_to_select_cpu), buf_cpu);
     warning("Final available CPUs:");
     for (int cpu = 0; cpu < cpus_num; cpu++) {
       if (CPU_ISSET(cpu, &new_cpu_mask)) {
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 6adb6bb1a..4a9f9b2bb 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -4135,6 +4135,17 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
     warning("dependency logging results may be inflated by VerifyDependencies");
   }
 
+#ifdef AARCH64
+  if (NUMANodes != NULL || NUMANodesRandom != 0) {
+    const char* numa_chosen_env = getenv("_JVM_NUMA_BINDING_DONE");
+    if (numa_chosen_env == NULL || strcmp(numa_chosen_env, "1") != 0) {
+      if (!UseNUMA) {
+        UseNUMA = true;
+      }
+    }
+  }
+#endif //AARCH64
+
   apply_debugger_ergo();
 
   if (log_is_enabled(Info, arguments)) {
diff --git a/test/hotspot/jtreg/containers/docker/Args.java b/test/hotspot/jtreg/containers/docker/Args.java
new file mode 100644
index 000000000..eda4f1459
--- /dev/null
+++ b/test/hotspot/jtreg/containers/docker/Args.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, 2025, Huawei Technologies Co., Ltd. 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class Args {
+    public static void main(String[] args) {
+        System.out.println("===== ARGS =====");
+        for (int i = 0; i < args.length; i++) {
+            System.out.println("args[" + i + "] = " + args[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/hotspot/jtreg/containers/docker/TestNUMABindPolicy.java b/test/hotspot/jtreg/containers/docker/TestNUMABindPolicy.java
new file mode 100644
index 000000000..ce52848d8
--- /dev/null
+++ b/test/hotspot/jtreg/containers/docker/TestNUMABindPolicy.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020, 2025, Huawei Technologies Co., Ltd. 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+* @test
+* @summary Test NUMA bind policy
+* @requires os.arch == "aarch64" & os.family == "linux"
+* @library /test/lib
+* @build Args
+* @run main/othervm/timeout=5000 TestNUMABindPolicy
+*/
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestNUMABindPolicy {
+    private static int getNUMANodeCount() {
+        try {
+            String content = new String(java.nio.file.Files.readAllBytes(
+                java.nio.file.Paths.get("/sys/devices/system/node/possible")));
+            // "0-1" or "0-3"
+            String[] parts = content.trim().split("-");
+            if (parts.length == 2) {
+                int maxNode = Integer.parseInt(parts[1]);
+                return maxNode + 1;
+            }
+        } catch (Exception e) {
+        }
+        return 0;
+    }
+
+    static void runAndCheckWithEqual(int mem, String policy, String prefix, boolean isSuccess, String expectedOut) throws Exception {
+        int cpu = 2;
+
+        int neededNodes = Math.max(cpu, mem);
+        int actualNodes = getNUMANodeCount();
+        if (actualNodes <= neededNodes) {
+            System.out.println("Skipping test: Need more than " + neededNodes +
+                             " NUMA nodes, but only " + actualNodes + " available");
+            return;
+        }
+
+        OutputAnalyzer out = ProcessTools.executeTestJava(
+                "-XX:+UseNUMA",
+                "-XX:+LogNUMANodes",
+                "-XX:NUMANodesRandom=" + cpu,
+                "-XX:NUMAMemNodesRandom=" + mem,
+                "-XX:NUMABindPolicy=" + policy,
+                "Args",
+                "--prefix" + prefix
+        );
+
+        if (isSuccess) {
+            out.shouldHaveExitValue(0);
+            out.shouldMatch(".*Successfully bound cpu to " + cpu + " node\\(s\\): ([0-9]+,){" + (cpu - 1) + "}[0-9]+");
+            if (cpu > mem) {
+                out.shouldMatch(".*Successfully bound mem to " + cpu + " node\\(s\\): ([0-9]+,){" + (cpu - 1) + "}[0-9]+");
+            } else {
+                out.shouldMatch(".*Successfully bound mem to " + mem + " node\\(s\\): ([0-9]+,){" + (mem - 1) + "}[0-9]+");
+            }
+        } else {
+            out.shouldHaveExitValue(0);
+            out.shouldContain(expectedOut);
+        }
+    }
+
+    static void runAndCheckWithoutEqual() throws Exception {
+        OutputAnalyzer out = ProcessTools.executeTestJava(
+                "-XX:+UseNUMA",
+                "-XX:+LogNUMANodes",
+                "-XX:NUMANodesRandom=" + 2,
+                "-XX:NUMAMemNodesRandom=" +2,
+                "-XX:NUMABindPolicy=prefix=--prefix,div=1",
+                "Args",
+                "--prefix",
+                "-1"
+        );
+
+        out.shouldHaveExitValue(0);
+        out.shouldContain("Invalid prefix id, feature disabled.");
+    }
+
+    public static void main(String[] args) throws Exception {
+        // test nodes bound to mem is no less than that to cpu
+        runAndCheckWithEqual(2, "prefix=--prefix,div=1", "=1", true, "");
+        runAndCheckWithEqual(3, "prefix=--prefix,div=1", "=1", true, "");
+
+        // test nodes bound to mem is less than that to cpu
+        runAndCheckWithEqual(1, "prefix=--prefix,div=1", "=1", true, "");
+
+        // test NUMABindPolicy is imcomplete
+        runAndCheckWithEqual(3, "prefix=--prefix,abc=1", "=1", false, "Lack of prefix/div. Feature disabled.");
+
+        // test with invalid prefix
+        runAndCheckWithEqual(3, "prefix=,div=1", "=1", false, "NUMABindPolicy: prefix cannot be empty.");
+        runAndCheckWithEqual(3, "prefix=abc,div=1", "=1", false, "Cannot find corresponding process according to prefix. Feature disabled.");
+        runAndCheckWithEqual(3, "prefix=--prefix,div=1", "", false, "Cannot find corresponding process according to prefix. Feature disabled.");
+        runAndCheckWithEqual(3, "prefix=--prefix,div=1", "=-1", false, "Invalid prefix id, feature disabled.");
+        runAndCheckWithoutEqual();
+
+        // test with invalid div
+        runAndCheckWithEqual(3, "prefix=--prefix,div=0", "=1", false, "NUMABindPolicy: div must be a positive integer, got '0'");
+        runAndCheckWithEqual(3, "prefix=--prefix,div=abc", "=1", false, "NUMABindPolicy: div must be a positive integer, got 'abc'");
+    }
+}
\ No newline at end of file
diff --git a/test/hotspot/jtreg/containers/docker/TestNUMANodesRandom.java b/test/hotspot/jtreg/containers/docker/TestNUMANodesRandom.java
index a8a1d7c5a..b01eb6c9f 100644
--- a/test/hotspot/jtreg/containers/docker/TestNUMANodesRandom.java
+++ b/test/hotspot/jtreg/containers/docker/TestNUMANodesRandom.java
@@ -124,7 +124,7 @@ public class TestNUMANodesRandom {
 
         output.shouldHaveExitValue(0);
         output.shouldContain("will select 1 nodes from");
-        output.shouldContain("Successfully bound to 1 node(s)");
+        output.shouldContain("Successfully bound cpu to 1 node(s)");
 
         System.out.println("PASS: Single node random selection works");
     }
@@ -142,7 +142,7 @@ public class TestNUMANodesRandom {
         output.shouldHaveExitValue(0);
         output.shouldContain("will select " + count + " nodes from");
         output.shouldContain("Distance from node");
-        output.shouldContain("Successfully bound to " + count + " node(s)");
+        output.shouldContain("Successfully bound cpu to " + count + " node(s)");
 
         System.out.println("PASS: Multiple nodes random selection works");
     }
diff --git a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java
index afe424896..58fdaacf8 100644
--- a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java
+++ b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java
@@ -89,6 +89,22 @@ public class TestTargetSurvivorRatioFlag {
 
         LinkedList<String> options = new LinkedList<>(Arrays.asList(Utils.getTestJavaOpts()));
 
+        int numaNodesRandomCpu = options.stream().filter(s -> s.startsWith("-XX:NUMANodesRandom="))
+                                        .findFirst().map(s -> Integer.parseInt(s.substring("-XX:NUMANodesRandom=".length())))
+                                        .orElse(-1);
+
+        int numaNodesRandomMem = options.stream().filter(s -> s.startsWith("-XX:NUMAMemNodesRandom="))
+                                        .findFirst().map(s -> Integer.parseInt(s.substring("-XX:NUMAMemNodesRandom=".length())))
+                                        .orElse(-1);
+
+        boolean useNUMA = options.contains("-XX:+UseNUMA");
+
+        // Avoid this binding policy because spreading memory to nodes not bound to the CPU nodes increases PLABWastePct.
+        // Using numactl --cpunodebind=0-m --membind=0-n (m < n) with -XX:+UseNUMA will trigger the same issue.
+        if (numaNodesRandomCpu != numaNodesRandomMem && useNUMA) {
+            return;
+        }
+
         // Need to consider the effect of TargetPLABWastePct=1 for G1 GC
         if (options.contains("-XX:+UseG1GC")) {
             VARIANCE = 2;
-- 
2.34.1