src/share/vm/services/memTracker.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File
*** old/src/share/vm/services/memTracker.cpp	Tue Jun  4 09:59:51 2013
--- new/src/share/vm/services/memTracker.cpp	Tue Jun  4 09:59:50 2013

*** 66,75 **** --- 66,76 ---- MemTracker::ShutdownReason MemTracker::_reason = NMT_shutdown_none; int MemTracker::_thread_count = 255; volatile jint MemTracker::_pooled_recorder_count = 0; volatile unsigned long MemTracker::_processing_generation = 0; volatile bool MemTracker::_worker_thread_idle = false; + volatile jint MemTracker::_pending_op_count = 0; volatile bool MemTracker::_slowdown_calling_thread = false; debug_only(intx MemTracker::_main_thread_tid = 0;) NOT_PRODUCT(volatile jint MemTracker::_pending_recorder_count = 0;) void MemTracker::init_tracking_options(const char* option_line) {
*** 326,421 **** --- 327,344 ---- rec->set_next(cur_head); } Atomic::inc(&_pooled_recorder_count); } /* * This is the most important method in whole nmt implementation. * * Create a memory record. * 1. When nmt is in single-threaded bootstrapping mode, no lock is needed as VM * still in single thread mode. * 2. For all threads other than JavaThread, ThreadCritical is needed * to write to recorders to global recorder. * 3. For JavaThreads that are not longer visible by safepoint, also * need to take ThreadCritical and records are written to global * recorders, since these threads are NOT walked by Threads.do_thread(). * 4. JavaThreads that are running in native state, have to transition * to VM state before writing to per-thread recorders. * 5. JavaThreads that are running in VM state do not need any lock and * records are written to per-thread recorders. * 6. For a thread has yet to attach VM 'Thread', they need to take * ThreadCritical to write to global recorder. * * Important note: * NO LOCK should be taken inside ThreadCritical lock !!! */ void MemTracker::create_memory_record(address addr, MEMFLAGS flags, size_t size, address pc, Thread* thread) { assert(addr != NULL, "Sanity check"); if (!shutdown_in_progress()) { // single thread, we just write records direct to global recorder,' // with any lock if (_state == NMT_bootstrapping_single_thread) { assert(_main_thread_tid == os::current_thread_id(), "wrong thread"); thread = NULL; } else { if (thread == NULL) { // don't use Thread::current(), since it is possible that // the calling thread has yet to attach to VM 'Thread', // which will result assertion failure thread = ThreadLocalStorage::thread(); } } if (thread != NULL) { // slow down all calling threads except NMT worker thread, so it // can catch up. if (_slowdown_calling_thread && thread != _worker_thread) { os::yield_all(); } if (thread->is_Java_thread() && ((JavaThread*)thread)->is_safepoint_visible()) { JavaThread* java_thread = (JavaThread*)thread; JavaThreadState state = java_thread->thread_state(); if (SafepointSynchronize::safepoint_safe(java_thread, state)) { // JavaThreads that are safepoint safe, can run through safepoint, // so ThreadCritical is needed to ensure no threads at safepoint create // new records while the records are being gathered and the sequence number is changing ThreadCritical tc; create_record_in_recorder(addr, flags, size, pc, java_thread); } else { create_record_in_recorder(addr, flags, size, pc, java_thread); } } else { // other threads, such as worker and watcher threads, etc. need to // take ThreadCritical to write to global recorder ThreadCritical tc; create_record_in_recorder(addr, flags, size, pc, NULL); } } else { if (_state == NMT_bootstrapping_single_thread) { // single thread, no lock needed create_record_in_recorder(addr, flags, size, pc, NULL); } else { // for thread has yet to attach VM 'Thread', we can not use VM mutex. // use native thread critical instead ThreadCritical tc; create_record_in_recorder(addr, flags, size, pc, NULL); } } } } // write a record to proper recorder. No lock can be taken from this method // down. ! void MemTracker::create_record_in_recorder(address addr, MEMFLAGS flags, ! size_t size, address pc, JavaThread* thread) { ! void MemTracker::write_tracking_record(address addr, MEMFLAGS flags, ! size_t size, jint seq, address pc, JavaThread* thread) { MemRecorder* rc = get_thread_recorder(thread); if (rc != NULL) { ! rc->record(addr, flags, size, seq, pc); } } /** * enqueue a recorder to pending queue
*** 476,492 **** --- 399,417 ---- if (per_seq_in_use < SAFE_SEQUENCE_THRESHOLD && per_gen_in_use >= HIGH_GENERATION_THRESHOLD) { _sync_point_skip_count ++; return; } } _sync_point_skip_count = 0; { // This method is running at safepoint, with ThreadCritical lock, // it should guarantee that NMT is fully sync-ed. ThreadCritical tc; + // We can NOT execute NMT sync-point if there are pending tracking ops. + if (_pending_op_count == 0) { SequenceGenerator::reset(); + _sync_point_skip_count = 0; // walk all JavaThreads to collect recorders SyncThreadRecorderClosure stc; Threads::threads_do(&stc);
*** 507,518 **** --- 432,445 ---- // check _worker_thread with lock to avoid racing condition if (_worker_thread != NULL) { _worker_thread->at_sync_point(pending_recorders, InstanceKlass::number_of_instance_classes()); } assert(SequenceGenerator::peek() == 1, "Should not have memory activities during sync-point"); + } else { + _sync_point_skip_count ++; + } } } // now, it is the time to shut whole things off if (_state == NMT_final_shutdown) {
*** 697,701 **** --- 624,862 ---- st->print_cr("No snapshot"); } } #endif + + // Tracker Implementation + + /* + * Create a tracker. + * This is a fairly complicated constructor, as it has to make two important decisions: + * 1) Does it need to take ThreadCritical lock to write tracking record + * 2) Does it need to pre-reserve a sequence number for the tracking record + * + * The rules to determine if ThreadCritical is needed: + * 1. When nmt is in single-threaded bootstrapping mode, no lock is needed as VM + * still in single thread mode. + * 2. For all threads other than JavaThread, ThreadCritical is needed + * to write to recorders to global recorder. + * 3. For JavaThreads that are not longer visible by safepoint, also + * need to take ThreadCritical and records are written to global + * recorders, since these threads are NOT walked by Threads.do_thread(). + * 4. JavaThreads that are running in safepoint-safe states do not stop + * for safepoints, ThreadCritical lock shoul be taken to write + * memory records. + * 5. JavaThreads that are running in VM state do not need any lock and + * records are written to per-thread recorders. + * 6. For a thread has yet to attach VM 'Thread', they need to take + * ThreadCritical to write to global recorder. + * + * The memory operations that need pre-reserve sequence numbers: + * The memory operations that "release" memory blocks and the + * operations can fail, need to pre-reserve sequence number. They + * are realloc, uncommit and release. + * + * The reason for pre-reserve sequence number, is to prevent race condition: + * Thread 1 Thread 2 + * <release> + * <allocate> + * <write allocate record> + * <write release record> + * if Thread 2 happens to obtain the memory address Thread 1 just released, + * then NMT can mistakenly report the memory is free. + * + * Noticeably, free() does not need pre-reserve sequence number, because the call + * does not fail, so we can alway write "release" record before the memory is actaully + * freed. + * + * For realloc, uncommit and release, following coding pattern should be used: + * + * MemTracker::Tracker tkr = MemTracker::get_realloc_tracker(); + * ptr = ::realloc(...); + * if (ptr == NULL) { + * tkr.record(...) + * } + * + * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker(); + * if (uncommit(...)) { + * tkr.record(...); + * } + * + * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + * if (release(...)) { + * tkr.record(...); + * } + * + * Since pre-reserved sequence number is only good for the generation that it is acquired, + * when there is pending Tracker that reserved sequence number, NMT sync-point has + * to be skipped to prevent from advancing generation. This is done by inc and dec + * MemTracker::_pending_op_count, when MemTracker::_pending_op_count > 0, NMT sync-point is skipped. + * Not all pre-reservation of sequence number will increment pending op count. For JavaThreads + * that honor safepoints, safepoint can not occur during the memory operations, so the + * pre-reserved sequence number won't cross the generation boundry. + */ + MemTracker::Tracker::Tracker(MemoryOperation op, Thread* thr) { + _op = NoOp; + _seq = 0; + if (MemTracker::is_on()) { + _java_thread = NULL; + _op = op; + + // figure out if ThreadCritical lock is needed to write this operation + // to MemTracker + if (MemTracker::is_single_threaded_bootstrap()) { + thr = NULL; + } else if (thr == NULL) { + // don't use Thread::current(), since it is possible that + // the calling thread has yet to attach to VM 'Thread', + // which will result assertion failure + thr = ThreadLocalStorage::thread(); + } + + if (thr != NULL) { + // Check NMT load + MemTracker::check_NMT_load(thr); + + if (thr->is_Java_thread() && ((JavaThread*)thr)->is_safepoint_visible()) { + _java_thread = (JavaThread*)thr; + JavaThreadState state = _java_thread->thread_state(); + // JavaThreads that are safepoint safe, can run through safepoint, + // so ThreadCritical is needed to ensure no threads at safepoint create + // new records while the records are being gathered and the sequence number is changing + _need_thread_critical_lock = + SafepointSynchronize::safepoint_safe(_java_thread, state); + } else { + _need_thread_critical_lock = true; + } + } else { + _need_thread_critical_lock + = !MemTracker::is_single_threaded_bootstrap(); + } + + // see if we need to pre-reserve sequence number for this operation + if (_op == Realloc || _op == Uncommit || _op == Release) { + if (_need_thread_critical_lock) { + ThreadCritical tc; + MemTracker::inc_pending_op_count(); + _seq = SequenceGenerator::next(); + } else { + // for the threads that honor safepoints, no safepoint can occur + // during the lifespan of tracker, so we don't need to increase + // pending op count. + _seq = SequenceGenerator::next(); + } + } + } + } + + void MemTracker::Tracker::discard() { + if (MemTracker::is_on() && _seq != 0) { + if (_need_thread_critical_lock) { + ThreadCritical tc; + MemTracker::dec_pending_op_count(); + } + _seq = 0; + } + } + + + void MemTracker::Tracker::record(address old_addr, address new_addr, size_t size, + MEMFLAGS flags, address pc) { + assert(old_addr != NULL && new_addr != NULL, "Sanity check"); + assert(_op == Realloc || _op == NoOp, "Wrong call"); + if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp) { + assert(_seq > 0, "Need pre-reserve sequence number"); + if (_need_thread_critical_lock) { + ThreadCritical tc; + // free old address, use pre-reserved sequence number + MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(), + 0, _seq, pc, _java_thread); + MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(), + size, SequenceGenerator::next(), pc, _java_thread); + // decrement MemTracker pending_op_count + MemTracker::dec_pending_op_count(); + } else { + // free old address, use pre-reserved sequence number + MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(), + 0, _seq, pc, _java_thread); + MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(), + size, SequenceGenerator::next(), pc, _java_thread); + } + _seq = 0; + } + } + + void MemTracker::Tracker::record(address addr, size_t size, MEMFLAGS flags, address pc) { + // OOM already? + if (addr == NULL) return; + + if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp) { + bool pre_reserved_seq = (_seq != 0); + address pc = CALLER_CALLER_PC; + MEMFLAGS orig_flags = flags; + + // or the tagging flags + switch(_op) { + case Malloc: + flags |= MemPointerRecord::malloc_tag(); + break; + case Free: + flags = MemPointerRecord::free_tag(); + break; + case Realloc: + fatal("Use the other Tracker::record()"); + break; + case Reserve: + case ReserveAndCommit: + flags |= MemPointerRecord::virtual_memory_reserve_tag(); + break; + case Commit: + flags = MemPointerRecord::virtual_memory_commit_tag(); + break; + case Type: + flags |= MemPointerRecord::virtual_memory_type_tag(); + break; + case Uncommit: + assert(pre_reserved_seq, "Need pre-reserve sequence number"); + flags = MemPointerRecord::virtual_memory_uncommit_tag(); + break; + case Release: + assert(pre_reserved_seq, "Need pre-reserve sequence number"); + flags = MemPointerRecord::virtual_memory_release_tag(); + break; + case ArenaSize: + // a bit of hack here, add a small postive offset to arena + // address for its size record, so the size record is sorted + // right after arena record. + flags = MemPointerRecord::arena_size_tag(); + addr += sizeof(void*); + break; + case StackRelease: + flags = MemPointerRecord::virtual_memory_release_tag(); + break; + default: + ShouldNotReachHere(); + } + + // write memory tracking record + if (_need_thread_critical_lock) { + ThreadCritical tc; + if (_seq == 0) _seq = SequenceGenerator::next(); + MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread); + if (_op == ReserveAndCommit) { + MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(), + size, SequenceGenerator::next(), pc, _java_thread); + } + if (pre_reserved_seq) MemTracker::dec_pending_op_count(); + } else { + if (_seq == 0) _seq = SequenceGenerator::next(); + MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread); + if (_op == ReserveAndCommit) { + MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(), + size, SequenceGenerator::next(), pc, _java_thread); + } + } + _seq = 0; + } + } +

src/share/vm/services/memTracker.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File