RFR: 8312132: Add tracking of multiple address spaces in NMT [v74]
Thomas Stuefe
stuefe at openjdk.org
Fri May 10 12:15:25 UTC 2024
On Wed, 8 May 2024 11:53:17 GMT, Johan Sjölen <jsjolen at openjdk.org> wrote:
>> Hi,
>>
>> This PR introduces a new abstraction to NMT, named `MemoryFileTracker`. Today, NMT does not track any memory outside of the virtual memory address space. This means that if you allocated memory in something such as a memory-backed file and use `mmap` to map into that memory, then you'll have trouble reporting this to NMT. This is the situation that ZGC is in, and that is what this patch attempts to fix.
>>
>> ## `MemoryFileTracker`
>>
>> The `MemoryFileTracker` adds the ability of adding new virtual memory address spaces to NMT and committing memory to these, the basic API is:
>>
>> ```c++
>> static MemoryFile* make_device(const char* descriptive_name);
>> static void free_device(MemoryFile* device);
>>
>> static void allocate_memory(MemoryFile* device, size_t offset, size_t size,
>> MEMFLAGS flag, const NativeCallStack& stack);
>> static void free_memory(MemoryFile* device, size_t offset, size_t size);
>>
>>
>> It is easiest to see how this is used by looking at what ZGC's `ZNMT` class does:
>>
>> ```c++
>> void ZNMT::reserve(zaddress_unsafe start, size_t size) {
>> MemTracker::record_virtual_memory_reserve((address)start, size, CALLER_PC, mtJavaHeap);
>> }
>> void ZNMT::commit(zoffset offset, size_t size) {
>> MemTracker::allocate_memory_in(ZNMT::_device, static_cast<size_t>(offset), size, mtJavaHeap, CALLER_PC);
>> }
>> void ZNMT::uncommit(zoffset offset, size_t size) {
>> MemTracker::free_memory_in(ZNMT::_device, (size_t)offset, size);
>> }
>>
>> void ZNMT::map(zaddress_unsafe addr, size_t size, zoffset offset) {
>> // NMT doesn't track mappings at the moment.
>> }
>> void ZNMT::unmap(zaddress_unsafe addr, size_t size) {
>> // NMT doesn't track mappings at the moment.
>> }
>>
>>
>> As you can see, any mapping between reserved regions and device-allocated memory is not recorded in NMT. This means that in detailed mode you only get reserved regions printed for the reserved memory, the device-allocated memory is reported separately. When performing summary reporting any memory allocated via these devices is added to the corresponding `MEMFLAGS` as `committed` memory.
>>
>> This patch is also acting as a base on which we deploy multiple new backend ideas to NMT. These ideas are:
>>
>> 1. Implement VMA tracking using a balanced binary tree approach. Today's `VirtualMemoryTracker`'s usage of linked lists is slow and brittle, we'd like to move away from it. Our Treap-based approach in this patch gives a performance bo...
>
> Johan Sjölen has updated the pull request incrementally with one additional commit since the last revision:
>
> Some style
More Treap reviewing, started with VMATree
src/hotspot/share/nmt/nmtTreap.hpp line 373:
> 371: }
> 372: }
> 373: };
`visit_range_in_order` looks okay, though it surely is a bit of a brain teaser. So the first half of the algorithm walks the outer-left flank of whatever the current head is, up to the point where we "stick a toe outside the range", then it repeats that part, basically, with the right node, which could be a way in, which is why we again have to walk the left flank.
I assume this is based on a paper, but trust you to have checked that. However, I would really like regression testing for this. Lots of range checks in gtest, please (can just be tacked onto what I did ask you for yesterday).
src/hotspot/share/nmt/virtualMemoryTracker.hpp line 33:
> 31: #include "nmt/allocationSite.hpp"
> 32: #include "nmt/nmtCommon.hpp"
> 33: #include "runtime/atomic.hpp"
Please add includes only where needed, directly. Let's not rely on indirect includes. Unless this is a remnant from some earlier version, then pls just remove it.
src/hotspot/share/nmt/vmatree.cpp line 2:
> 1: /*
> 2: * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
Be a dear and add us, please :-)
src/hotspot/share/nmt/vmatree.cpp line 156:
> 154: if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) {
> 155: // We must have smashed a hole in an existing region (or replaced it entirely).
> 156: // LEQ_A - A - B - (some node >= B)
nit, clearer comment (a bit) since at first glance looks like substraction: `LEQ_A < A < B < (some node >= B)`. Alternatively, `LEQ_A [A, B) C`
src/hotspot/share/nmt/vmatree.cpp line 198:
> 196:
> 197: // Finally, we can register the new region [A, B)'s summary data.
> 198: auto& rescom = diff.flag[NMTUtil::flag_to_index(metadata.flag)];
Can we please not use `auto` when unnecessary? IDEs have enough of a hard time understanding our code. `SingleDiff&`, please.
src/hotspot/share/nmt/vmatree.hpp line 67:
> 65:
> 66: Metadata(NativeCallStackStorage::StackIndex stack_idx, MEMFLAGS flag)
> 67: : stack_idx(stack_idx), flag(flag) {}
I would assert here that with state=released, we only ever want to see mtNone.
src/hotspot/share/nmt/vmatree.hpp line 91:
> 89: StateType type() const {
> 90: return static_cast<StateType>(type_flag[0]);
> 91: }
Proposal: provide `is_reserved()` and `is_committed()` and replace manual comparisons with the state enum with those. Easier on the eye.
src/hotspot/share/nmt/vmatree.hpp line 114:
> 112: bool is_noop() {
> 113: return (in.type() == StateType::Released && out.type() == StateType::Released) ||
> 114: (in.type() == out.type() && Metadata::equals(in.metadata(), out.metadata()));
We require a released region to be mtNone, state Released. You ensure so below. So here, you should just need the second condition, no special handling for released regions needed.
src/hotspot/share/nmt/vmatree.hpp line 117:
> 115: }
> 116: };
> 117:
Do `IntervalState` and `IntervalChange` need to be exposed in the header?
src/hotspot/share/nmt/vmatree.hpp line 151:
> 149:
> 150: SummaryDiff release_mapping(position from, position sz) {
> 151: Metadata empty;
Just a nit, but instead of the Metadata::Metadata() ctor creating an empty object, could we possibly scrap the default ctor and have an explicit static constexpr Metadata empty with invalid stackindex and mtNone as ctor args? I find that nicer to read.
-------------
PR Review: https://git.openjdk.org/jdk/pull/18289#pullrequestreview-2049875773
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596632543
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596633794
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596649353
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596665032
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596670430
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596647037
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596667575
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596645720
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596659540
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1596644523
More information about the hotspot-dev
mailing list