Skip to content

Commit bb377b2

Browse files
committed
8306841: Generational ZGC: NMT reports Java heap size larger than max heap size
Reviewed-by: eosterlund, stuefe
1 parent ac3ce2b commit bb377b2

File tree

2 files changed

+110
-24
lines changed

2 files changed

+110
-24
lines changed

src/hotspot/share/gc/z/zPhysicalMemory.cpp

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ void ZPhysicalMemoryManager::try_enable_uncommit(size_t min_capacity, size_t max
276276
}
277277

278278
void ZPhysicalMemoryManager::nmt_commit(zoffset offset, size_t size) const {
279+
// NMT expects a 1-to-1 mapping between virtual and physical memory.
280+
// ZGC can temporarily have multiple virtual addresses pointing to
281+
// the same physical memory.
282+
//
283+
// When this function is called we don't know where in the virtual memory
284+
// this physical memory will be mapped. So we fake that the virtual memory
285+
// address is the heap base + the given offset.
279286
const zaddress addr = ZOffset::address(offset);
280287
MemTracker::record_virtual_memory_commit((void*)untype(addr), size, CALLER_PC);
281288
}
@@ -320,6 +327,11 @@ bool ZPhysicalMemoryManager::commit(ZPhysicalMemory& pmem) {
320327

321328
// Commit segment
322329
const size_t committed = _backing.commit(segment.start(), segment.size());
330+
331+
// Register with NMT
332+
nmt_commit(segment.start(), committed);
333+
334+
// Register committed segment
323335
if (!pmem.commit_segment(i, committed)) {
324336
// Failed or partially failed
325337
return false;
@@ -341,6 +353,11 @@ bool ZPhysicalMemoryManager::uncommit(ZPhysicalMemory& pmem) {
341353

342354
// Uncommit segment
343355
const size_t uncommitted = _backing.uncommit(segment.start(), segment.size());
356+
357+
// Unregister with NMT
358+
nmt_uncommit(segment.start(), uncommitted);
359+
360+
// Deregister uncommitted segment
344361
if (!pmem.uncommit_segment(i, uncommitted)) {
345362
// Failed or partially failed
346363
return false;
@@ -351,12 +368,16 @@ bool ZPhysicalMemoryManager::uncommit(ZPhysicalMemory& pmem) {
351368
return true;
352369
}
353370

354-
void ZPhysicalMemoryManager::pretouch_view(zaddress addr, size_t size) const {
371+
void ZPhysicalMemoryManager::pretouch(zoffset offset, size_t size) const {
372+
const uintptr_t addr = untype(ZOffset::address(offset));
355373
const size_t page_size = ZLargePages::is_explicit() ? ZGranuleSize : os::vm_page_size();
356-
os::pretouch_memory((void*)untype(addr), (void*)(untype(addr) + size), page_size);
374+
os::pretouch_memory((void*)addr, (void*)(addr + size), page_size);
357375
}
358376

359-
void ZPhysicalMemoryManager::map_view(zaddress_unsafe addr, const ZPhysicalMemory& pmem) const {
377+
// Map virtual memory to physcial memory
378+
void ZPhysicalMemoryManager::map(zoffset offset, const ZPhysicalMemory& pmem) const {
379+
const zaddress_unsafe addr = ZOffset::address_unsafe(offset);
380+
360381
size_t size = 0;
361382

362383
// Map segments
@@ -375,27 +396,9 @@ void ZPhysicalMemoryManager::map_view(zaddress_unsafe addr, const ZPhysicalMemor
375396
}
376397
}
377398

378-
void ZPhysicalMemoryManager::unmap_view(zaddress_unsafe addr, size_t size) const {
379-
_backing.unmap(addr, size);
380-
}
381-
382-
void ZPhysicalMemoryManager::pretouch(zoffset offset, size_t size) const {
383-
// Pre-touch all views
384-
pretouch_view(ZOffset::address(offset), size);
385-
}
386-
387-
void ZPhysicalMemoryManager::map(zoffset offset, const ZPhysicalMemory& pmem) const {
388-
const size_t size = pmem.size();
389-
390-
// Map all views
391-
map_view(ZOffset::address_unsafe(offset), pmem);
392-
393-
nmt_commit(offset, size);
394-
}
395-
399+
// Unmap virtual memory from physical memory
396400
void ZPhysicalMemoryManager::unmap(zoffset offset, size_t size) const {
397-
nmt_uncommit(offset, size);
401+
const zaddress_unsafe addr = ZOffset::address_unsafe(offset);
398402

399-
// Unmap all views
400-
unmap_view(ZOffset::address_unsafe(offset), size);
403+
_backing.unmap(addr, size);
401404
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2023, 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
26+
* @bug 8306841
27+
* @summary Sanity check Java Heap size values
28+
* @modules java.base/jdk.internal.misc
29+
* @library /test/lib
30+
* @run driver NMTJavaHeapTest
31+
*/
32+
33+
import jdk.test.lib.process.ProcessTools;
34+
import jdk.test.lib.Asserts;
35+
import jdk.test.lib.Utils;
36+
import jdk.test.lib.process.OutputAnalyzer;
37+
38+
public class NMTJavaHeapTest {
39+
public static void main(String args[]) throws Exception {
40+
ProcessBuilder pb = ProcessTools.createTestJvm(
41+
"-XX:+UnlockDiagnosticVMOptions",
42+
"-XX:+PrintNMTStatistics",
43+
"-XX:NativeMemoryTracking=summary",
44+
"-version");
45+
46+
OutputAnalyzer output = new OutputAnalyzer(pb.start());
47+
48+
// Java Heap (reserved=786432KB, committed=49152KB)
49+
String pattern = ".*Java Heap \\(reserved=.*, committed=(.*)\\).*";
50+
String committed = output.firstMatch(pattern, 1);
51+
Asserts.assertNotNull(committed, "Couldn't find pattern '" + pattern
52+
+ "': in output '" + output.getOutput() + "'");
53+
54+
long committedBytes = committedStringToBytes(committed);
55+
56+
// Must be more than zero
57+
Asserts.assertGT(committedBytes, 0L);
58+
59+
// Compare against the max heap size
60+
long maxBytes = Runtime.getRuntime().maxMemory();
61+
Asserts.assertLTE(committedBytes, maxBytes);
62+
}
63+
64+
private static long K = 1024;
65+
private static long M = K * 1024;
66+
private static long G = M * 1024;
67+
68+
private static long committedStringToBytes(String committed) {
69+
long multiplier = 1;
70+
if (committed.endsWith("GB")) {
71+
multiplier = G;
72+
committed = committed.replace("GB", "");
73+
} else if (committed.endsWith("MB")) {
74+
multiplier = M;
75+
committed = committed.replace("MB", "");
76+
} else if (committed.endsWith("KB")) {
77+
multiplier = K;
78+
committed = committed.replace("KB", "");
79+
}
80+
81+
return Long.parseLong(committed) * multiplier;
82+
}
83+
}

0 commit comments

Comments
 (0)