Skip to content

Instantly share code, notes, and snippets.

@rednaxelafx
Created March 28, 2012 04:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rednaxelafx/2223542 to your computer and use it in GitHub Desktop.
Save rednaxelafx/2223542 to your computer and use it in GitHub Desktop.
ThreadMXBean and blocked monitor notes

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(&reg_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;
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment