Skip to content

Commit a66fe2a

Browse files
committed
8366434: THP not working properly with G1 after JDK-8345655
Backport-of: a03302d41bb9971736d4d56381ca0cad1eb3e34b
1 parent 9a0e2ef commit a66fe2a

File tree

3 files changed

+156
-2
lines changed

3 files changed

+156
-2
lines changed

src/hotspot/share/memory/memoryReserver.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,13 @@ static char* reserve_memory_inner(char* requested_address,
113113
ReservedSpace MemoryReserver::reserve_memory(char* requested_address,
114114
size_t size,
115115
size_t alignment,
116+
size_t page_size,
116117
bool exec,
117118
MemTag mem_tag) {
118119
char* base = reserve_memory_inner(requested_address, size, alignment, exec, mem_tag);
119120

120121
if (base != nullptr) {
121-
return ReservedSpace(base, size, alignment, os::vm_page_size(), exec, false /* special */);
122+
return ReservedSpace(base, size, alignment, page_size, exec, false /* special */);
122123
}
123124

124125
// Failed
@@ -191,7 +192,7 @@ ReservedSpace MemoryReserver::reserve(char* requested_address,
191192
}
192193

193194
// == Case 3 ==
194-
return reserve_memory(requested_address, size, alignment, executable, mem_tag);
195+
return reserve_memory(requested_address, size, alignment, page_size, executable, mem_tag);
195196
}
196197

197198
ReservedSpace MemoryReserver::reserve(char* requested_address,

src/hotspot/share/memory/memoryReserver.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class MemoryReserver : AllStatic {
3434
static ReservedSpace reserve_memory(char* requested_address,
3535
size_t size,
3636
size_t alignment,
37+
size_t page_size,
3738
bool exec,
3839
MemTag mem_tag);
3940

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test id=G1
26+
* @summary Run tests with G1
27+
* @library /test/lib
28+
* @build jdk.test.lib.Platform
29+
* @requires os.family == "linux"
30+
* @requires vm.gc.G1
31+
* @run driver TestTransparentHugePagesHeap G1
32+
*/
33+
/*
34+
* @test id=Parallel
35+
* @summary Run tests with Parallel
36+
* @library /test/lib
37+
* @build jdk.test.lib.Platform
38+
* @requires os.family == "linux"
39+
* @requires vm.gc.Parallel
40+
* @run driver TestTransparentHugePagesHeap Parallel
41+
*/
42+
/*
43+
* @test id=Serial
44+
* @summary Run tests with Serial
45+
* @library /test/lib
46+
* @build jdk.test.lib.Platform
47+
* @requires os.family == "linux"
48+
* @requires vm.gc.Serial
49+
* @run driver TestTransparentHugePagesHeap Serial
50+
*/
51+
52+
import java.nio.file.Files;
53+
import java.nio.file.Path;
54+
import java.nio.file.Paths;
55+
import java.nio.file.StandardCopyOption;
56+
import java.util.regex.Matcher;
57+
import java.util.regex.Pattern;
58+
import java.util.Scanner;
59+
60+
import jdk.test.lib.os.linux.HugePageConfiguration;
61+
import jdk.test.lib.process.OutputAnalyzer;
62+
import jdk.test.lib.process.ProcessTools;
63+
import jdk.test.lib.Platform;
64+
65+
import jtreg.SkippedException;
66+
67+
// We verify that the heap can be backed by THP by looking at the
68+
// THPeligible field for the heap section in /proc/self/smaps. This
69+
// field indicates if a mapping can use THP.
70+
// THP mode 'always': this field is 1 whenever huge pages can be used
71+
// THP mode 'madvise': this field is 1 if the mapping has been madvised
72+
// as MADV_HUGEPAGE. In the JVM that should happen when the flag
73+
// -XX:+UseTransparentHugePages is specified.
74+
//
75+
// Note: we don't verify if the heap is backed by huge pages because we
76+
// can't know if the underlying system have any available.
77+
public class TestTransparentHugePagesHeap {
78+
79+
public static void main(String args[]) throws Exception {
80+
// To be able to detect large page use (esp. THP) somewhat reliably, we
81+
// need at least kernel 3.8 to get the "VmFlags" tag in smaps.
82+
// (Note: its still good we started the VM at least since this serves as a nice
83+
// test for all manners of large page options).
84+
if (Platform.getOsVersionMajor() < 3 ||
85+
(Platform.getOsVersionMajor() == 3 && Platform.getOsVersionMinor() < 8)) {
86+
throw new SkippedException("Kernel older than 3.8 - skipping this test.");
87+
}
88+
89+
final HugePageConfiguration hugePageConfiguration = HugePageConfiguration.readFromOS();
90+
if (!hugePageConfiguration.supportsTHP()) {
91+
throw new SkippedException("THP is turned off");
92+
}
93+
94+
OutputAnalyzer oa = ProcessTools.executeTestJava("-XX:+Use" + args[0] + "GC", "-Xmx128m", "-Xms128m", "-Xlog:pagesize:thp-%p.log", "-XX:+UseTransparentHugePages", VerifyTHPEnabledForHeap.class.getName());
95+
oa.shouldHaveExitValue(0);
96+
}
97+
98+
class VerifyTHPEnabledForHeap {
99+
100+
public static void main(String args[]) throws Exception {
101+
String heapAddress = readHeapAddressInLog();
102+
Path smaps = makeSmapsCopy();
103+
104+
final Pattern heapSection = Pattern.compile("^" + heapAddress + ".*");
105+
final Pattern thpEligible = Pattern.compile("THPeligible:\\s+(\\d)\\s*");
106+
107+
Scanner smapsFile = new Scanner(smaps);
108+
while (smapsFile.hasNextLine()) {
109+
Matcher heapMatcher = heapSection.matcher(smapsFile.nextLine());
110+
111+
if (heapMatcher.matches()) {
112+
// Found the first heap section, verify that it is THP eligible
113+
while (smapsFile.hasNextLine()) {
114+
Matcher m = thpEligible.matcher(smapsFile.nextLine());
115+
if (m.matches()) {
116+
if (Integer.parseInt(m.group(1)) == 1) {
117+
// THPeligible is 1, heap can be backed by huge pages
118+
return;
119+
}
120+
121+
throw new RuntimeException("First heap section at 0x" + heapAddress + " is not THPeligible");
122+
}
123+
}
124+
}
125+
}
126+
127+
// Failed to verify THP for heap
128+
throw new RuntimeException("Could not find heap section in smaps file");
129+
}
130+
131+
private static String readHeapAddressInLog() throws Exception {
132+
final Pattern heapAddress = Pattern.compile(".* Heap: .*base=(0x[0-9A-Fa-f]*).*");
133+
134+
Scanner logFile = new Scanner(Paths.get("thp-" + ProcessHandle.current().pid() + ".log"));
135+
while (logFile.hasNextLine()) {
136+
Matcher m = heapAddress.matcher(logFile.nextLine());
137+
if (m.matches()) {
138+
return Long.toHexString(Long.decode(m.group(1)));
139+
}
140+
}
141+
throw new RuntimeException("Failed to parse heap address, failing test");
142+
}
143+
144+
private static Path makeSmapsCopy() throws Exception {
145+
Path src = Paths.get("/proc/self/smaps");
146+
Path dest = Paths.get("smaps-copy-" + ProcessHandle.current().pid() + ".txt");
147+
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
148+
return dest;
149+
}
150+
}
151+
}
152+

0 commit comments

Comments
 (0)