Saturday Apr 07, 2012

Object Query Language Help

We can use a query language called Object Query Language with JHat and Serviceability Agent to query information from Java Heap. Help on this language is available with the JHat tool. I have put this handy help document on OQL here for quick reference.

Sunday May 16, 2010

SA-Plugin for VisualVM

We all know Serviceability Agent - a great debugging tool for troubleshooting HotSpot VM problems.

Now Serviceability Agent is also available in VisualVM through SAPlugin. More deatils here:

Friday May 11, 2007

JVMTI agent to print reference paths

To get started on JVMTI please read JVMTI reference guide JVMTI reference guide and a very good article on JVMTI Programming here

Here I will briefly describe how I used JVMTI to write an agent which prints the reference paths of instances of a given class in the running process.

The global data structure used in this agent:
/\* Global agent data structure \*/
typedef struct {
    /\* JVMTI Environment \*/
    jvmtiEnv      \*jvmti;

    /\* Data access Lock \*/
    jrawMonitorID  lock;

    /\* Fields to store the Options passed to the agent\*/
    char\* classname;   
    int  max_count;    

    /\* boolen to indicate if dump is in progress \*/	
    jboolean dumpInProgress;
    JavaVM\* jvm;
    jclass  klass;
    jlong  klassTag;
} GlobalAgentData;

This agent prints these reference paths when a SIGQUIT signal is sent to the process. For this, In Agent_OnLoad() function, set the callback for DataDumpRequest and enable the event notification for JVMTI_EVENT_DATA_DUMP_REQUEST event.
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM \*jvm, char \*options, void \*reserved)
  callbacks.DataDumpRequest = &dataDumpRequest;

With the above code, when the process receives SIGQUIT (ctrl+\\ on solaris and ctrl+break on windows) signal, dataDumpRequest() function of this agent would get invoked.

Let's see how this function looks:
static void JNICALL dataDumpRequest(jvmtiEnv \*jvmti)
  enter_critical_section(jvmti); {
    if ( !gdata->dumpInProgress ) {
      gdata->dumpInProgress = JNI_TRUE;
      gdata->klassTag = 1;
      gdata->jvmti->SetTag(gdata->klass, gdata->klassTag);
      jint count = 0;
      void\* user_data = NULL;

      // iterate over all the reachable objects in heap
      jvmti->IterateOverReachableObjects(&heap_root_callback, &stack_ref_callback,
                 &object_ref_callback, user_data);

      // print ref paths
      RefPaths\* list = ref_paths;
      int max = gdata->max_count;

      printf("Reference paths of instances of %s ....\\n", gdata->classname);
      while ((list != NULL) && (max >= 0) ) {
        ObjectInfo\* object = (ObjectInfo\*)list->path;
        printf("\\n\\nReference Path:");
        list = list->next;

      // unset tags
      jvmti->IterateOverReachableObjects(&heap_root_callback, &stack_ref_callback,
            &object_ref_clean_callback, user_data);

      // delete object info list
      ObjectInfoList\* list1 = objList;
      while (list1) {
        ObjectInfoList\* node = list1;
        list1 = list1->next;
      objList = NULL;
      // delete ref paths list
      list = ref_paths;
      RefPaths\* path;

      while (list != NULL) {
        path = list;
        list = list->next;
      ref_paths = NULL;

      gdata->klassTag = 1;
      gdata->dumpInProgress = JNI_FALSE;
  } exit_critical_section(jvmti);	
Here, we first set the tag of the class gdata->klass which was set to the classname passed as parameter to the agent. Then we call IterateOverReachableObjects function to go over all the reachable objects in heap. IterateOverReachableObjects function iterates over the root objects and all objects that are directly and indirectly reachable from the root objects. In dataDumpRequest we are making two calls to IterateOverReachableObjects, first one to create a list of reachable objects and their referrers and the second one to unset the tags set in the first call.

IterateOverReachableObjects accepts three callbacks, callback which gets called when any root object is visited, callback which gets called when any stack object is visited and a callback when any object reference is visited. This sample agent does not do anything in the root and stack callbacks.

Now let's see what the object reference callback does:
static jvmtiIterationControl JNICALL
object_ref_callback(jvmtiObjectReferenceKind reference_kind,
		jlong class_tag, jlong size, jlong\* tag_ptr,
		jlong referrer_tag, jint referrer_index, void \*user_data)
  ObjectInfo\* obj_info = NULL;
  // if this object's tag is set which can be for the class we want to create
  // reference paths for or if we are visiting this object again.
  if (\*tag_ptr != NULL) { 
    if (gdata->klassTag == 1) {
      if (\*tag_ptr == gdata->klassTag) {
        obj_info = new ObjectInfo();
	memset(obj_info, 0 , sizeof(ObjectInfo));
	obj_info->size = size;
	obj_info->visited = 1;
	obj_info->kind = reference_kind;

	\*tag_ptr = (jlong)(ptrdiff_t)(void\*)obj_info;
	gdata->klassTag = \*tag_ptr;
    } else {
      obj_info = (ObjectInfo\*)\*tag_ptr;
      if (obj_info->visited == 1)
  // if tag is not present, then create ObjectInfo and set it as it's tag.
  else {
    obj_info = new ObjectInfo();
    memset(obj_info, 0 , sizeof(ObjectInfo));
    obj_info->size = size;
    obj_info->visited = 1;
    obj_info->kind = reference_kind;

    \*tag_ptr = (jlong)(ptrdiff_t)(void\*)obj_info;

    //Add the new ObjectInfo to ObjectInfo's list
    if (objList == NULL) {
      objList = new ObjectInfoList();
      objList->obj = obj_info;
      objList->next = NULL;
    } else {
      ObjectInfoList\* list = objList;
      while (list->next != NULL) {
        list = list->next;
      ObjectInfoList\* objinfo = new ObjectInfoList();
      objinfo->obj = obj_info;
      objinfo->next = NULL;

      list->next = objinfo;
  // If this object has the referrer then put it in the referrers list
  if (referrer_tag != NULL) {
    if (obj_info->referrers == NULL) {
      obj_info->referrers = new Referrer();
      obj_info->referrers->tag = referrer_tag;
      obj_info->referrers->refereeTag = \*tag_ptr;
      obj_info->referrers->next = NULL;
    } else {
      Referrer\* referrer = obj_info->referrers;
      while (referrer->next != NULL) {
        referrer = referrer->next;
      Referrer\* ref = new Referrer();
      ref->tag = referrer_tag;
      ref->refereeTag = \*tag_ptr;
      ref->next = NULL;
      referrer->next = ref;			

  if (class_tag == gdata->klassTag) {


Here the ObjectInfo class represents an object in the heap. We tag every visited reference with the ObjectInfo pointer and when it's refree is visited, the referer_tag contains this ObjectInfo pointer. So by using tag_ptr and referer_tag, this callback creates list of reference paths (RefPaths) of all the instances of the given class.

After returning from the callback, function dfsPrintRefPaths prints prints these reference paths. Then we make another call to IterateOverReachableObjects to unset the tags we had set in the first iteration.

The full code of this agent can be downloaded from here.

How this works:
Build ref_paths.cpp into a shared library. Copy the library refpaths.dll or in the current working directory or set the LD_LIBRARY_PATH on solaris and PATH env variable on Windows to the loaction of this library.

Start your java program with options:
java -agentlib:refpaths=classname="java/lang/String",max=40 TestClass

where -agentlib:refpaths specifies that 'refpaths' is the jvmti based shared library
and following that are the arguments passed to this agent.
o classname - name of the class for which we want to print the ref-paths
o max - the maximum number of instances

The above commnad will print the 40 ref-paths of instances of java/lang/String class.

To print the ref-paths at any point, send Sigquit signal to the running process
and it will print the paths though which the requested instances are reachable.

The output of ref-paths printed by this agent looks like this:
Reference Path:
- Instance Field: /E:/jre1.5.0_10/lib/ext/dnsns.jar
- Array Element: file:/E:/jre1.5.0_10/lib/ext/dnsns.jar
- Instance Field: [Ljava.lang.Object;@1c78e57
- Instance Field: [file:/E:/jre1.5.0_10/lib/ext/dnsns.jar, file:/E:/jre1.5.0_10/lib/ext/localedata.jar, file:/E:/jre1.5.0_10/lib/ext/sunjce_provider.jar, file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar]
- Instance Field: sun.misc.URLClassPath@5224ee
- Instance Field: sun.misc.Launcher$ExtClassLoader@92e78c
- ClassLoader: sun.misc.Launcher$AppClassLoader@a39137
- class TestClass

Reference Path:
- Instance Field: file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar!/
- Instance Field: jar:file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar!/
- Array Element: sun.misc.URLClassPath$JarLoader@f6a746
- Instance Field: [Ljava.lang.Object;@15ff48b
- Instance Field: [sun.misc.URLClassPath$JarLoader@affc70, sun.misc.URLClassPath
- Instance Field: sun.misc.URLClassPath@5224ee
- Instance Field: sun.misc.Launcher$ExtClassLoader@92e78c
- ClassLoader: sun.misc.Launcher$AppClassLoader@a39137
- class TestClass

where any entry is reachable through the entry below it.



« July 2016