ThreadMXBean from JMX can reliably dump inflated ("contended") monitors, including those locked from native frames.
Implementation details:
src/share/classes/java/lang/management/ManagementFactory.java
public static ThreadMXBean getThreadMXBean() {
return sun.management.ManagementFactory.getThreadMXBean();
}
src/share/classes/sun/management/ManagementFactory.java
public static synchronized ThreadMXBean getThreadMXBean() {
if (threadMBean == null) {
threadMBean = new ThreadImpl(jvm);
}
return threadMBean;
}
src/share/classes/sun/management/ThreadImpl.java
public ThreadInfo[] getThreadInfo(long[] ids,
boolean lockedMonitors,
boolean lockedSynchronizers) {
verifyThreadIds(ids);
verifyDumpThreads(lockedMonitors, lockedSynchronizers);
return dumpThreads0(ids, lockedMonitors, lockedSynchronizers);
}
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
boolean lockedSynchronizers) {
verifyDumpThreads(lockedMonitors, lockedSynchronizers);
return dumpThreads0(null, lockedMonitors, lockedSynchronizers);
}
private static native ThreadInfo[] dumpThreads0(long[] ids,
boolean lockedMonitors,
boolean lockedSynchronizers);
src/share/native/sun/management/ThreadImpl.c
JNIEXPORT jobjectArray JNICALL
Java_sun_management_ThreadImpl_dumpThreads0
(JNIEnv *env, jclass cls, jlongArray ids, jboolean lockedMonitors, jboolean lockedSynchronizers)
{
return jmm_interface->DumpThreads(env, ids, lockedMonitors, lockedSynchronizers);
}
hotspot/src/share/vm/services/management.cpp
// Dump thread info for the specified threads.
// It returns an array of ThreadInfo objects. Each element is the ThreadInfo
// for the thread ID specified in the corresponding entry in
// the given array of thread IDs; or NULL if the thread does not exist
// or has terminated.
//
// Input parameter:
// ids - array of thread IDs; NULL indicates all live threads
// locked_monitors - if true, dump locked object monitors
// locked_synchronizers - if true, dump locked JSR-166 synchronizers
//
JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboolean locked_monitors, jboolean locked_synchronizers))
ResourceMark rm(THREAD);
if (JDK_Version::is_gte_jdk16x_version()) {
// make sure the AbstractOwnableSynchronizer klass is loaded before taking thread snapshots
java_util_concurrent_locks_AbstractOwnableSynchronizer::initialize(CHECK_NULL);
}
typeArrayOop ta = typeArrayOop(JNIHandles::resolve(thread_ids));
int num_threads = (ta != NULL ? ta->length() : 0);
typeArrayHandle ids_ah(THREAD, ta);
ThreadDumpResult dump_result(num_threads); // can safepoint
if (ids_ah() != NULL) {
// validate the thread id array
validate_thread_id_array(ids_ah, CHECK_NULL);
// obtain thread dump of a specific list of threads
do_thread_dump(&dump_result,
ids_ah,
num_threads,
-1, /* entire stack */
(locked_monitors ? true : false), /* with locked monitors */
(locked_synchronizers ? true : false), /* with locked synchronizers */
CHECK_NULL);
} else {
// obtain thread dump of all threads
VM_ThreadDump op(&dump_result,
-1, /* entire stack */
(locked_monitors ? true : false), /* with locked monitors */
(locked_synchronizers ? true : false) /* with locked synchronizers */);
VMThread::execute(&op);
}
int num_snapshots = dump_result.num_snapshots();
// create the result ThreadInfo[] object
klassOop k = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL);
instanceKlassHandle ik (THREAD, k);
objArrayOop r = oopFactory::new_objArray(ik(), num_snapshots, CHECK_NULL);
objArrayHandle result_h(THREAD, r);
int index = 0;
for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; ts = ts->next(), index++) {
if (ts->threadObj() == NULL) {
// if the thread does not exist or now it is terminated, set threadinfo to NULL
result_h->obj_at_put(index, NULL);
continue;
}
ThreadStackTrace* stacktrace = ts->get_stack_trace();
assert(stacktrace != NULL, "Must have a stack trace dumped");
// Create Object[] filled with locked monitors
// Create int[] filled with the stack depth where a monitor was locked
int num_frames = stacktrace->get_stack_depth();
int num_locked_monitors = stacktrace->num_jni_locked_monitors();
// Count the total number of locked monitors
for (int i = 0; i < num_frames; i++) {
StackFrameInfo* frame = stacktrace->stack_frame_at(i);
num_locked_monitors += frame->num_locked_monitors();
}
objArrayHandle monitors_array;
typeArrayHandle depths_array;
objArrayHandle synchronizers_array;
if (locked_monitors) {
// Constructs Object[] and int[] to contain the object monitor and the stack depth
// where the thread locked it
objArrayOop array = oopFactory::new_system_objArray(num_locked_monitors, CHECK_NULL);
objArrayHandle mh(THREAD, array);
monitors_array = mh;
typeArrayOop tarray = oopFactory::new_typeArray(T_INT, num_locked_monitors, CHECK_NULL);
typeArrayHandle dh(THREAD, tarray);
depths_array = dh;
int count = 0;
int j = 0;
for (int depth = 0; depth < num_frames; depth++) {
StackFrameInfo* frame = stacktrace->stack_frame_at(depth);
int len = frame->num_locked_monitors();
GrowableArray<oop>* locked_monitors = frame->locked_monitors();
for (j = 0; j < len; j++) {
oop monitor = locked_monitors->at(j);
assert(monitor != NULL && monitor->is_instance(), "must be a Java object");
monitors_array->obj_at_put(count, monitor);
depths_array->int_at_put(count, depth);
count++;
}
}
GrowableArray<oop>* jni_locked_monitors = stacktrace->jni_locked_monitors();
for (j = 0; j < jni_locked_monitors->length(); j++) {
oop object = jni_locked_monitors->at(j);
assert(object != NULL && object->is_instance(), "must be a Java object");
monitors_array->obj_at_put(count, object);
// Monitor locked via JNI MonitorEnter call doesn't have stack depth info
depths_array->int_at_put(count, -1);
count++;
}
assert(count == num_locked_monitors, "number of locked monitors doesn't match");
}
if (locked_synchronizers) {
// Create Object[] filled with locked JSR-166 synchronizers
assert(ts->threadObj() != NULL, "Must be a valid JavaThread");
ThreadConcurrentLocks* tcl = ts->get_concurrent_locks();
GrowableArray<instanceOop>* locks = (tcl != NULL ? tcl->owned_locks() : NULL);
int num_locked_synchronizers = (locks != NULL ? locks->length() : 0);
objArrayOop array = oopFactory::new_system_objArray(num_locked_synchronizers, CHECK_NULL);
objArrayHandle sh(THREAD, array);
synchronizers_array = sh;
for (int k = 0; k < num_locked_synchronizers; k++) {
synchronizers_array->obj_at_put(k, locks->at(k));
}
}
// Create java.lang.management.ThreadInfo object
instanceOop info_obj = Management::create_thread_info_instance(ts,
monitors_array,
depths_array,
synchronizers_array,
CHECK_NULL);
result_h->obj_at_put(index, info_obj);
}
return (jobjectArray) JNIHandles::make_local(env, result_h());
JVM_END
const struct jmmInterface_1_ jmm_interface = {
NULL,
NULL,
jmm_GetVersion,
jmm_GetOptionalSupport,
jmm_GetInputArguments,
jmm_GetThreadInfo,
jmm_GetInputArgumentArray,
jmm_GetMemoryPools,
jmm_GetMemoryManagers,
jmm_GetMemoryPoolUsage,
jmm_GetPeakMemoryPoolUsage,
jmm_GetThreadAllocatedMemory,
jmm_GetMemoryUsage,
jmm_GetLongAttribute,
jmm_GetBoolAttribute,
jmm_SetBoolAttribute,
jmm_GetLongAttributes,
jmm_FindMonitorDeadlockedThreads,
jmm_GetThreadCpuTime,
jmm_GetVMGlobalNames,
jmm_GetVMGlobals,
jmm_GetInternalThreadTimes,
jmm_ResetStatistic,
jmm_SetPoolSensor,
jmm_SetPoolThreshold,
jmm_GetPoolCollectionUsage,
jmm_GetGCExtAttributeInfo,
jmm_GetLastGCStat,
jmm_GetThreadCpuTimeWithKind,
jmm_GetThreadCpuTimesWithKind,
jmm_DumpHeap0,
jmm_FindDeadlockedThreads,
jmm_SetVMGlobal,
NULL,
jmm_DumpThreads
};
hotspot/src/share/vm/runtime/vm_operations.cpp
void VM_ThreadDump::doit() {
ResourceMark rm;
ConcurrentLocksDump concurrent_locks(true);
if (_with_locked_synchronizers) {
concurrent_locks.dump_at_safepoint();
}
if (_num_threads == 0) {
// Snapshot all live threads
for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
if (jt->is_exiting() ||
jt->is_hidden_from_external_view()) {
// skip terminating threads and hidden threads
continue;
}
ThreadConcurrentLocks* tcl = NULL;
if (_with_locked_synchronizers) {
tcl = concurrent_locks.thread_concurrent_locks(jt);
}
ThreadSnapshot* ts = snapshot_thread(jt, tcl);
_result->add_thread_snapshot(ts);
}
} else {
// Snapshot threads in the given _threads array
// A dummy snapshot is created if a thread doesn't exist
for (int i = 0; i < _num_threads; i++) {
instanceHandle th = _threads->at(i);
if (th() == NULL) {
// skip if the thread doesn't exist
// Add a dummy snapshot
_result->add_thread_snapshot(new ThreadSnapshot());
continue;
}
// Dump thread stack only if the thread is alive and not exiting
// and not VM internal thread.
JavaThread* jt = java_lang_Thread::thread(th());
if (jt == NULL || /* thread not alive */
jt->is_exiting() ||
jt->is_hidden_from_external_view()) {
// add a NULL snapshot if skipped
_result->add_thread_snapshot(new ThreadSnapshot());
continue;
}
ThreadConcurrentLocks* tcl = NULL;
if (_with_locked_synchronizers) {
tcl = concurrent_locks.thread_concurrent_locks(jt);
}
ThreadSnapshot* ts = snapshot_thread(jt, tcl);
_result->add_thread_snapshot(ts);
}
}
}
ThreadSnapshot* VM_ThreadDump::snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl) {
ThreadSnapshot* snapshot = new ThreadSnapshot(java_thread);
snapshot->dump_stack_at_safepoint(_max_depth, _with_locked_monitors);
snapshot->set_concurrent_locks(tcl);
return snapshot;
}
hotspot/src/share/vm/services/threadService.cpp
// Iterate through monitor cache to find JNI locked monitors
class InflatedMonitorsClosure: public MonitorClosure {
private:
ThreadStackTrace* _stack_trace;
Thread* _thread;
public:
InflatedMonitorsClosure(Thread* t, ThreadStackTrace* st) {
_thread = t;
_stack_trace = st;
}
void do_monitor(ObjectMonitor* mid) {
if (mid->owner() == _thread) {
oop object = (oop) mid->object();
if (!_stack_trace->is_owned_monitor_on_stack(object)) {
_stack_trace->add_jni_locked_monitor(object);
}
}
}
};
void ThreadStackTrace::dump_stack_at_safepoint(int maxDepth) {
assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped");
if (_thread->has_last_Java_frame()) {
RegisterMap reg_map(_thread);
vframe* start_vf = _thread->last_java_vframe(®_map);
int count = 0;
for (vframe* f = start_vf; f; f = f->sender() ) {
if (f->is_java_frame()) {
javaVFrame* jvf = javaVFrame::cast(f);
add_stack_frame(jvf);
count++;
} else {
// Ignore non-Java frames
}
if (maxDepth > 0 && count == maxDepth) {
// Skip frames if more than maxDepth
break;
}
}
}
if (_with_locked_monitors) {
// Iterate inflated monitors and find monitors locked by this thread
// not found in the stack
InflatedMonitorsClosure imc(_thread, this);
ObjectSynchronizer::monitors_iterate(&imc);
}
}
bool ThreadStackTrace::is_owned_monitor_on_stack(oop object) {
assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped");
bool found = false;
int num_frames = get_stack_depth();
for (int depth = 0; depth < num_frames; depth++) {
StackFrameInfo* frame = stack_frame_at(depth);
int len = frame->num_locked_monitors();
GrowableArray<oop>* locked_monitors = frame->locked_monitors();
for (int j = 0; j < len; j++) {
oop monitor = locked_monitors->at(j);
assert(monitor != NULL && monitor->is_instance(), "must be a Java object");
if (monitor == object) {
found = true;
break;
}
}
}
return found;
}
hotspot/src/share/vm/runtime/synchronizer.cpp
void ObjectSynchronizer::monitors_iterate(MonitorClosure* closure) {
ObjectMonitor* block = gBlockList;
ObjectMonitor* mid;
while (block) {
assert(block->object() == CHAINMARKER, "must be a block header");
for (int i = _BLOCKSIZE - 1; i > 0; i--) {
mid = block + i;
oop object = (oop) mid->object();
if (object != NULL) {
closure->do_monitor(mid);
}
}
block = (ObjectMonitor*) block->FreeNext;
}
}