RFR: 8312132: Add tracking of multiple address spaces in NMT [v49]

Gerard Ziemski gziemski at openjdk.org
Tue Apr 23 20:39:33 UTC 2024


On Tue, 23 Apr 2024 13:44:59 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:
> 
>   Move TreapNode into Treap

This is a partial review. I will work more on it tomorrow.

I have few comments, but at this point what stands out to me the most is having to use:

`MemoryFileTracker::Instance`

Can we remove the `Instance` ? We don't need to use explicit instances in other NMT components.

src/hotspot/share/nmt/memReporter.cpp line 914:

> 912:     MemoryFileTracker::Instance::print_report_on(dev, this->output(), scale());
> 913:   }
> 914: }

Does `devices.length()` and `devices.at(i)` are really needed to be exposed?

Can we consider pushing all this inside `MemoryFileTracker`?

We make 4 different API calls to MemoryFileTracker here in such a small function.

src/hotspot/share/nmt/memTracker.cpp line 71:

> 69:     if (!MallocTracker::initialize(level) ||
> 70:         !VirtualMemoryTracker::initialize(level) ||
> 71:         !MemoryFileTracker::Instance::initialize(level) ||

Is there a way to hide the `instance` so that we could do:

`!MemoryFileTracker::initialize(level)
`

not

`!MemoryFileTracker::Instance::initialize(level)`

just like the other calls here? The instance is not needed here and just an implementation detail.

src/hotspot/share/nmt/memTracker.hpp line 172:

> 170:   static inline MemoryFileTracker::MemoryFile* register_device(const char* descriptive_name) {
> 171:     assert_post_init();
> 172:     if (!enabled()) return nullptr;

Could we push `assert_post_init()` into `enabled()` ?

-------------

Changes requested by gziemski (Committer).

PR Review: https://git.openjdk.org/jdk/pull/18289#pullrequestreview-2018212344
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1576843510
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1576837099
PR Review Comment: https://git.openjdk.org/jdk/pull/18289#discussion_r1576859789


More information about the hotspot-dev mailing list