-
Notifications
You must be signed in to change notification settings - Fork 6k
8346123: [REDO] NMT should not use ThreadCritical #22745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a20de0c
e817cf1
c169ff8
1d07d95
a51cdc6
ab18d2b
9015d98
f519c3b
5af32fc
547f07b
9e016b8
223928d
1909279
5e36546
379eaac
0304668
6e7bf18
3fbacd6
57d0fba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,6 @@ | |
#include "nmt/threadStackTracker.hpp" | ||
#include "nmt/virtualMemoryTracker.hpp" | ||
#include "runtime/mutexLocker.hpp" | ||
#include "runtime/threadCritical.hpp" | ||
#include "utilities/debug.hpp" | ||
#include "utilities/nativeCallStack.hpp" | ||
|
||
|
@@ -62,6 +61,12 @@ class MemTracker : AllStatic { | |
return _tracking_level != NMT_unknown; | ||
} | ||
|
||
// This may be called on a detached thread during VM init, so we should check that first. | ||
static inline void assert_locked() { | ||
assert(!NmtVirtualMemoryLocker::is_safe_to_use() || NmtVirtualMemory_lock->owned_by_self(), | ||
"should have acquired NmtVirtualMemory_lock"); | ||
} | ||
|
||
static inline NMT_TrackingLevel tracking_level() { | ||
return _tracking_level; | ||
} | ||
|
@@ -125,7 +130,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
if (addr != nullptr) { | ||
ThreadCritical tc; | ||
NmtVirtualMemoryLocker nvml; | ||
VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag); | ||
} | ||
} | ||
|
@@ -151,7 +156,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
if (addr != nullptr) { | ||
ThreadCritical tc; | ||
NmtVirtualMemoryLocker nvml; | ||
VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag); | ||
VirtualMemoryTracker::add_committed_region((address)addr, size, stack); | ||
} | ||
|
@@ -162,23 +167,23 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
if (addr != nullptr) { | ||
ThreadCritical tc; | ||
NmtVirtualMemoryLocker nvml; | ||
VirtualMemoryTracker::add_committed_region((address)addr, size, stack); | ||
} | ||
} | ||
|
||
static inline MemoryFileTracker::MemoryFile* register_file(const char* descriptive_name) { | ||
assert_post_init(); | ||
if (!enabled()) return nullptr; | ||
MemoryFileTracker::Instance::Locker lock; | ||
NmtVirtualMemoryLocker nvml; | ||
return MemoryFileTracker::Instance::make_file(descriptive_name); | ||
} | ||
|
||
static inline void remove_file(MemoryFileTracker::MemoryFile* file) { | ||
assert_post_init(); | ||
if (!enabled()) return; | ||
assert(file != nullptr, "must be"); | ||
MemoryFileTracker::Instance::Locker lock; | ||
NmtVirtualMemoryLocker nvml; | ||
MemoryFileTracker::Instance::free_file(file); | ||
} | ||
|
||
|
@@ -187,7 +192,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
assert(file != nullptr, "must be"); | ||
MemoryFileTracker::Instance::Locker lock; | ||
NmtVirtualMemoryLocker nvml; | ||
MemoryFileTracker::Instance::allocate_memory(file, offset, size, stack, mem_tag); | ||
} | ||
|
||
|
@@ -196,7 +201,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
assert(file != nullptr, "must be"); | ||
MemoryFileTracker::Instance::Locker lock; | ||
NmtVirtualMemoryLocker nvml; | ||
MemoryFileTracker::Instance::free_memory(file, offset, size); | ||
} | ||
|
||
|
@@ -210,7 +215,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
if (addr != nullptr) { | ||
ThreadCritical tc; | ||
NmtVirtualMemoryLocker nvml; | ||
VirtualMemoryTracker::split_reserved_region((address)addr, size, split, mem_tag, split_tag); | ||
} | ||
} | ||
|
@@ -219,7 +224,7 @@ class MemTracker : AllStatic { | |
assert_post_init(); | ||
if (!enabled()) return; | ||
if (addr != nullptr) { | ||
ThreadCritical tc; | ||
NmtVirtualMemoryLocker nvml; | ||
VirtualMemoryTracker::set_reserved_region_type((address)addr, mem_tag); | ||
} | ||
} | ||
|
@@ -269,6 +274,39 @@ class MemTracker : AllStatic { | |
// and return true; false if not found. | ||
static bool print_containing_region(const void* p, outputStream* out); | ||
|
||
/* | ||
* NmtVirtualMemoryLocker is similar to MutexLocker but can be used during VM init before mutexes are ready or | ||
* current thread has been assigned. Performs no action during VM init. | ||
* | ||
* Unlike malloc, NMT requires locking for virtual memory operations. This is because it must synchronize the usage | ||
* of global data structures used for modelling the effect of virtual memory operations. | ||
* It is important that locking is used such that the actual OS memory operations (mmap) are done atomically with the | ||
* corresponding NMT accounting (updating the internal model). Currently, this is not the case in all situations | ||
* (see JDK-8341491), but this should be changed in the future. | ||
* | ||
* An issue with using Mutex is that NMT is used early during VM initialization before mutexes are initialized | ||
* and current thread is attached. Mutexes do not work under those conditions, so we must use a flag to avoid | ||
* attempting to lock until initialization is finished. Lack of synchronization here should not be a problem since it | ||
* is single threaded at that point in time anyway. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good comment |
||
class NmtVirtualMemoryLocker: StackObj { | ||
// Returns true if it is safe to start using this locker. | ||
static bool _safe_to_use; | ||
ConditionalMutexLocker _cml; | ||
|
||
public: | ||
NmtVirtualMemoryLocker(): _cml(NmtVirtualMemory_lock, _safe_to_use, Mutex::_no_safepoint_check_flag){} | ||
|
||
static inline bool is_safe_to_use() { | ||
return _safe_to_use; | ||
} | ||
|
||
// Set in Threads::create_vm once threads and mutexes have been initialized. | ||
static inline void set_safe_to_use() { | ||
_safe_to_use = true; | ||
} | ||
}; | ||
|
||
private: | ||
static void report(bool summary_only, outputStream* output, size_t scale); | ||
|
||
|
@@ -277,8 +315,6 @@ class MemTracker : AllStatic { | |
static NMT_TrackingLevel _tracking_level; | ||
// Stored baseline | ||
static MemBaseline _baseline; | ||
// Query lock | ||
static Mutex* _query_lock; | ||
}; | ||
|
||
#endif // SHARE_NMT_MEMTRACKER_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,6 @@ | |
#include "nmt/threadStackTracker.hpp" | ||
#include "nmt/virtualMemoryTracker.hpp" | ||
#include "runtime/os.hpp" | ||
#include "runtime/threadCritical.hpp" | ||
#include "utilities/ostream.hpp" | ||
|
||
VirtualMemorySnapshot VirtualMemorySummary::_snapshot; | ||
|
@@ -338,6 +337,8 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, | |
assert(base_addr != nullptr, "Invalid address"); | ||
assert(size > 0, "Invalid size"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is kind of long. And why the space before the comma? And there's a bunch of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I've gotten rid of this in favor of the helper that Coleen suggested below. |
||
ReservedMemoryRegion rgn(base_addr, size, stack, mem_tag); | ||
ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); | ||
|
||
|
@@ -416,6 +417,7 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, | |
void VirtualMemoryTracker::set_reserved_region_type(address addr, MemTag mem_tag) { | ||
assert(addr != nullptr, "Invalid address"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could add this to MemTracker class like:
Then put the body
Then all these calls could be MemTracker::assert_locked(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. I've adopted your suggestion. Thank you! |
||
ReservedMemoryRegion rgn(addr, 1); | ||
ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); | ||
|
@@ -434,6 +436,7 @@ bool VirtualMemoryTracker::add_committed_region(address addr, size_t size, | |
assert(addr != nullptr, "Invalid address"); | ||
assert(size > 0, "Invalid size"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
ReservedMemoryRegion rgn(addr, size); | ||
ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); | ||
|
@@ -454,6 +457,7 @@ bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) | |
assert(addr != nullptr, "Invalid address"); | ||
assert(size > 0, "Invalid size"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
ReservedMemoryRegion rgn(addr, size); | ||
ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); | ||
|
@@ -469,6 +473,7 @@ bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) | |
bool VirtualMemoryTracker::remove_released_region(ReservedMemoryRegion* rgn) { | ||
assert(rgn != nullptr, "Sanity check"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
// uncommit regions within the released region | ||
ReservedMemoryRegion backup(*rgn); | ||
|
@@ -490,6 +495,7 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { | |
assert(addr != nullptr, "Invalid address"); | ||
assert(size > 0, "Invalid size"); | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
MemTracker::assert_locked(); | ||
|
||
ReservedMemoryRegion rgn(addr, size); | ||
ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); | ||
|
@@ -621,6 +627,9 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker { | |
SnapshotThreadStackWalker() {} | ||
|
||
bool do_allocation_site(const ReservedMemoryRegion* rgn) { | ||
if (MemTracker::NmtVirtualMemoryLocker::is_safe_to_use()) { | ||
assert_lock_strong(NmtVirtualMemory_lock); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (rgn->mem_tag() == mtThreadStack) { | ||
address stack_bottom = rgn->thread_stack_uncommitted_bottom(); | ||
address committed_start; | ||
|
@@ -661,7 +670,7 @@ void VirtualMemoryTracker::snapshot_thread_stacks() { | |
|
||
bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) { | ||
assert(_reserved_regions != nullptr, "Sanity check"); | ||
ThreadCritical tc; | ||
MemTracker::NmtVirtualMemoryLocker nvml; | ||
// Check that the _reserved_regions haven't been deleted. | ||
if (_reserved_regions != nullptr) { | ||
LinkedListNode<ReservedMemoryRegion>* head = _reserved_regions->head(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer "bootstrap_done" rather than "done_bootstrap" throughout. But you should get
opinions from some of the runtime folks like @coleenp and @dholmes-ora .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree, bootstrap_done reads better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a few other is_bootstrapping() predicates in the code like Universe::is_bootstrapping. This might read nicely as is_bootstrapping_done() and the instance is _bootstrapping_done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I've updated everything to use "bootstrapping_done" instead of "done_bootstrap"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguably, MemTracker is the wrong place anyway since the problem it tries to solve is not tied to NMT initialization.
The thought behind the "bootstrap_done" was to make visible that it is safe to not lock since we still run effectively single-threaded. Hotspot loading and CreateJavaVM may happen on different threads, but not at the same time.
We can delay real locking until other threads are starting up. That is somewhere in the middle of VM initialization, but long after NMT has booted. But we can do it also as soon as Mutex initialization is done.
So we could either move this to the mutex layer and give us a "MutexLocker::all_initialised". In that case, the implicit assumption is that as long as the JVM initializes this before other threads are started. Or, we could move it to the thread subsystem and give us a marker for when we are not single-threaded anymore. I like the former better.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??? What are you referring to as "hotspot loading"? To me that is CreateJavaVM.
We already have
Threads::number_of_threads()
.