3131#include " nmt/threadStackTracker.hpp"
3232#include " nmt/virtualMemoryTracker.hpp"
3333#include " runtime/mutexLocker.hpp"
34- #include " runtime/threadCritical.hpp"
3534#include " utilities/debug.hpp"
3635#include " utilities/nativeCallStack.hpp"
3736
@@ -62,6 +61,12 @@ class MemTracker : AllStatic {
6261 return _tracking_level != NMT_unknown;
6362 }
6463
64+ // This may be called on a detached thread during VM init, so we should check that first.
65+ static inline void assert_locked () {
66+ assert (!NmtVirtualMemoryLocker::is_safe_to_use () || NmtVirtualMemory_lock->owned_by_self (),
67+ " should have acquired NmtVirtualMemory_lock" );
68+ }
69+
6570 static inline NMT_TrackingLevel tracking_level () {
6671 return _tracking_level;
6772 }
@@ -125,7 +130,7 @@ class MemTracker : AllStatic {
125130 assert_post_init ();
126131 if (!enabled ()) return ;
127132 if (addr != nullptr ) {
128- ThreadCritical tc ;
133+ NmtVirtualMemoryLocker nvml ;
129134 VirtualMemoryTracker::add_reserved_region ((address)addr, size, stack, mem_tag);
130135 }
131136 }
@@ -151,7 +156,7 @@ class MemTracker : AllStatic {
151156 assert_post_init ();
152157 if (!enabled ()) return ;
153158 if (addr != nullptr ) {
154- ThreadCritical tc ;
159+ NmtVirtualMemoryLocker nvml ;
155160 VirtualMemoryTracker::add_reserved_region ((address)addr, size, stack, mem_tag);
156161 VirtualMemoryTracker::add_committed_region ((address)addr, size, stack);
157162 }
@@ -162,23 +167,23 @@ class MemTracker : AllStatic {
162167 assert_post_init ();
163168 if (!enabled ()) return ;
164169 if (addr != nullptr ) {
165- ThreadCritical tc ;
170+ NmtVirtualMemoryLocker nvml ;
166171 VirtualMemoryTracker::add_committed_region ((address)addr, size, stack);
167172 }
168173 }
169174
170175 static inline MemoryFileTracker::MemoryFile* register_file (const char * descriptive_name) {
171176 assert_post_init ();
172177 if (!enabled ()) return nullptr ;
173- MemoryFileTracker::Instance::Locker lock ;
178+ NmtVirtualMemoryLocker nvml ;
174179 return MemoryFileTracker::Instance::make_file (descriptive_name);
175180 }
176181
177182 static inline void remove_file (MemoryFileTracker::MemoryFile* file) {
178183 assert_post_init ();
179184 if (!enabled ()) return ;
180185 assert (file != nullptr , " must be" );
181- MemoryFileTracker::Instance::Locker lock ;
186+ NmtVirtualMemoryLocker nvml ;
182187 MemoryFileTracker::Instance::free_file (file);
183188 }
184189
@@ -187,7 +192,7 @@ class MemTracker : AllStatic {
187192 assert_post_init ();
188193 if (!enabled ()) return ;
189194 assert (file != nullptr , " must be" );
190- MemoryFileTracker::Instance::Locker lock ;
195+ NmtVirtualMemoryLocker nvml ;
191196 MemoryFileTracker::Instance::allocate_memory (file, offset, size, stack, mem_tag);
192197 }
193198
@@ -196,7 +201,7 @@ class MemTracker : AllStatic {
196201 assert_post_init ();
197202 if (!enabled ()) return ;
198203 assert (file != nullptr , " must be" );
199- MemoryFileTracker::Instance::Locker lock ;
204+ NmtVirtualMemoryLocker nvml ;
200205 MemoryFileTracker::Instance::free_memory (file, offset, size);
201206 }
202207
@@ -210,7 +215,7 @@ class MemTracker : AllStatic {
210215 assert_post_init ();
211216 if (!enabled ()) return ;
212217 if (addr != nullptr ) {
213- ThreadCritical tc ;
218+ NmtVirtualMemoryLocker nvml ;
214219 VirtualMemoryTracker::split_reserved_region ((address)addr, size, split, mem_tag, split_tag);
215220 }
216221 }
@@ -219,7 +224,7 @@ class MemTracker : AllStatic {
219224 assert_post_init ();
220225 if (!enabled ()) return ;
221226 if (addr != nullptr ) {
222- ThreadCritical tc ;
227+ NmtVirtualMemoryLocker nvml ;
223228 VirtualMemoryTracker::set_reserved_region_type ((address)addr, mem_tag);
224229 }
225230 }
@@ -269,6 +274,39 @@ class MemTracker : AllStatic {
269274 // and return true; false if not found.
270275 static bool print_containing_region (const void * p, outputStream* out);
271276
277+ /*
278+ * NmtVirtualMemoryLocker is similar to MutexLocker but can be used during VM init before mutexes are ready or
279+ * current thread has been assigned. Performs no action during VM init.
280+ *
281+ * Unlike malloc, NMT requires locking for virtual memory operations. This is because it must synchronize the usage
282+ * of global data structures used for modelling the effect of virtual memory operations.
283+ * It is important that locking is used such that the actual OS memory operations (mmap) are done atomically with the
284+ * corresponding NMT accounting (updating the internal model). Currently, this is not the case in all situations
285+ * (see JDK-8341491), but this should be changed in the future.
286+ *
287+ * An issue with using Mutex is that NMT is used early during VM initialization before mutexes are initialized
288+ * and current thread is attached. Mutexes do not work under those conditions, so we must use a flag to avoid
289+ * attempting to lock until initialization is finished. Lack of synchronization here should not be a problem since it
290+ * is single threaded at that point in time anyway.
291+ */
292+ class NmtVirtualMemoryLocker : StackObj {
293+ // Returns true if it is safe to start using this locker.
294+ static bool _safe_to_use;
295+ ConditionalMutexLocker _cml;
296+
297+ public:
298+ NmtVirtualMemoryLocker (): _cml(NmtVirtualMemory_lock, _safe_to_use, Mutex::_no_safepoint_check_flag){}
299+
300+ static inline bool is_safe_to_use () {
301+ return _safe_to_use;
302+ }
303+
304+ // Set in Threads::create_vm once threads and mutexes have been initialized.
305+ static inline void set_safe_to_use () {
306+ _safe_to_use = true ;
307+ }
308+ };
309+
272310 private:
273311 static void report (bool summary_only, outputStream* output, size_t scale);
274312
@@ -277,8 +315,6 @@ class MemTracker : AllStatic {
277315 static NMT_TrackingLevel _tracking_level;
278316 // Stored baseline
279317 static MemBaseline _baseline;
280- // Query lock
281- static Mutex* _query_lock;
282318};
283319
284320#endif // SHARE_NMT_MEMTRACKER_HPP
0 commit comments