8221517: G1: Reserved page size for heap can be wrong
authorsangheki
Mon, 01 Apr 2019 14:54:48 -0700
changeset 54368 f15b5d110fbc
parent 54367 e2c096943ba2
child 54369 66185e52b979
8221517: G1: Reserved page size for heap can be wrong Reviewed-by: tschatzl, kbarrett
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
test/hotspot/jtreg/gc/g1/TestLargePageUseForHeap.java
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Mon Apr 01 14:34:24 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Mon Apr 01 14:54:48 2019 -0700
@@ -1575,7 +1575,10 @@
     //    And ReservedSpace calls it 'special'. If we failed to set 'special',
     //    we reserved memory without large page.
     if (os::can_commit_large_page_memory() || rs.special()) {
-      page_size = rs.alignment();
+      // An alignment at ReservedSpace comes from preferred page size or
+      // heap alignment, and if the alignment came from heap alignment, it could be
+      // larger than large pages size. So need to cap with the large page size.
+      page_size = MIN2(rs.alignment(), os::large_page_size());
     }
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/TestLargePageUseForHeap.java	Mon Apr 01 14:54:48 2019 -0700
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019, 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
+ * 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.
+ */
+
+package gc.g1;
+
+/*
+ * @test TestLargePageUseForHeap.java
+ * @summary Test that Java heap is allocated using large pages of the appropriate size if available.
+ * @bug 8221517
+ * @key gc
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @requires vm.gc.G1
+ * @requires os.family != "solaris"
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+        -XX:+IgnoreUnrecognizedVMOptions -XX:+UseLargePages gc.g1.TestLargePageUseForHeap
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jtreg.SkippedException;
+import sun.hotspot.WhiteBox;
+
+public class TestLargePageUseForHeap {
+    static long largePageSize;
+    static long smallPageSize;
+
+    static void checkSize(OutputAnalyzer output, long expectedSize, String pattern) {
+        String pageSizeStr = output.firstMatch(pattern, 1);
+
+        if (pageSizeStr == null) {
+            output.reportDiagnosticSummary();
+            throw new RuntimeException("Match from '" + pattern + "' got 'null' expected: " + expectedSize);
+        }
+
+        long size = parseMemoryString(pageSizeStr);
+        if (size != expectedSize) {
+            output.reportDiagnosticSummary();
+            throw new RuntimeException("Match from '" + pattern + "' got " + size + " expected: " + expectedSize);
+        }
+    }
+
+    static boolean checkLargePageEnabled(OutputAnalyzer output) {
+        // This message is printed when tried to reserve a memory with large page but it failed.
+        String errorStr = "Reserve regular memory without large pages";
+        String heapPattern = ".*Heap: ";
+        // If errorStr is printed just before heap page log, reservation for Java Heap is failed.
+        String result = output.firstMatch(errorStr + "\n" + heapPattern);
+        if (result != null) {
+            return false;
+        }
+        return true;
+    }
+
+    static void checkHeap(OutputAnalyzer output, long expectedPageSize) throws Exception {
+        checkSize(output, expectedPageSize, "Heap: .*page_size=([^ ]+)");
+    }
+
+    static void testVM(long regionSize) throws Exception {
+        ProcessBuilder pb;
+        // Test with large page enabled.
+        pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+                                                   "-XX:G1HeapRegionSize=" + regionSize,
+                                                   "-Xmx128m",
+                                                   "-Xlog:pagesize,gc+heap+coops=debug",
+                                                   "-XX:+UseLargePages",
+                                                   "-version");
+
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        boolean largePageEnabled = checkLargePageEnabled(output);
+        checkHeap(output, largePageEnabled ? largePageSize : smallPageSize);
+        output.shouldHaveExitValue(0);
+
+        // Test with large page disabled.
+        pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+                                                   "-XX:G1HeapRegionSize=" + regionSize,
+                                                   "-Xmx128m",
+                                                   "-Xlog:pagesize,gc+heap+coops=debug",
+                                                   "-XX:-UseLargePages",
+                                                   "-version");
+
+        output = new OutputAnalyzer(pb.start());
+        checkHeap(output, smallPageSize);
+        output.shouldHaveExitValue(0);
+    }
+
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        smallPageSize = wb.getVMPageSize();
+        largePageSize = wb.getVMLargePageSize();
+
+        if (largePageSize == 0) {
+            throw new SkippedException("Large page support does not seem to be available on this platform.");
+        }
+        if (largePageSize == smallPageSize) {
+            throw new SkippedException("Large page support does not seem to be available on this platform."
+                    + "Small and large page size are the same.");
+        }
+
+        // G1HeapRegionSize=1MB
+        testVM(1 * 1024 * 1024);
+
+        // G1HeapRegionSize=2MB
+        testVM(2 * 1024 * 1024);
+
+        // G1HeapRegionSize=8MB
+        testVM(8 * 1024 * 1024);
+    }
+
+    public static long parseMemoryString(String value) {
+        long multiplier = 1;
+
+        if (value.endsWith("B")) {
+            multiplier = 1;
+        } else if (value.endsWith("K")) {
+            multiplier = 1024;
+        } else if (value.endsWith("M")) {
+            multiplier = 1024 * 1024;
+        } else if (value.endsWith("G")) {
+            multiplier = 1024 * 1024 * 1024;
+        } else {
+            throw new IllegalArgumentException("Expected memory string '" + value + "'to end with either of: B, K, M, G");
+        }
+
+        long longValue = Long.parseUnsignedLong(value.substring(0, value.length() - 1));
+
+        return longValue * multiplier;
+    }
+}
+