Implementation of Java thread monitoring based on JVMTI

Source: Internet
Author: User
Tags event listener instance method strcmp thread class

With the increasing popularity of multicore CPUs, more and more Java applications use multi-threaded parallel computing to give full play to the performance of the entire system. The use of multithreading also poses a huge challenge for application developers, and improper use of multithreading can lead to thread deadlock or resource contention, leading to system paralysis. Therefore, a run-time thread monitoring tool is required to help developers diagnose and track the switching of Java thread state. JDK 1.5 and its subsequent versions provide an interface JVMTI to monitor the running state of the virtual machine. This paper analyzes the Java threading model in the JVM, designs the model for monitoring the switch of thread state, and implements the agent for monitoring Java thread switching based on JVMTI.

1 reviews

Li Ling, software architect, IBM

December 10, 2009

    • Content

Develop and deploy your next application on the IBM Bluemix cloud platform.

Get started with your trial

JVMTI Tool Interface

With the development of multi-core CPU technology, multi-threaded programming technology is widely used to give full play to the performance of the whole system. The Java language provides language-level support for multithreaded programming, making it easy to create, run, and destroy threads. However, the use of multithreading also poses a huge challenge to application developers, and improper use of multithreading can lead to thread deadlock or resource contention, leading to system paralysis.

To help Java developers diagnose and track the switching of Java thread State, Sun has introduced the Java Virtual Machine tool interface (Java Vir) in the Java SDK (JAVA2 Software Development Kit, JDK) 1.5.0 version Tual Machine Toolkit interface,jvmti), used to replace the Java Virtual machine profiling interface that existed as a pilot function in previous JDK versions (Java VM Profiling INTERFACE,JVM PI) and Java VM Debug Interface (Java Virtual machine debugging Interface,jvmdi). The JVMTI interface allows you to create agents to monitor and control Java applications, including profiling, debugging, monitoring, parsing threads, and so on, as shown in Schema Model 1.

Figure 1. JVMTI Architecture Model

The agent can subscribe to an event of interest to a running virtual machine instance, and when these events occur, the agents are activated in the same way as the event callback function, while JVMTI provides a number of function functions to query and control the running state of the Java application. The agent communicates with the virtual machine through the interface provided by JVMTI, and synchronously monitors the running state of the virtual machine, which is relatively independent from the running Java application and does not interfere with the normal operation of the program. The Agent can be written in any native language that supports the C language standard and exists as a dynamic link library, which can be loaded when the Java program is started.

The Agent built on the JVMTI interface facilitates the tracking of Java thread State switching, enabling developers to clearly understand the threads of a multithreaded application at run time, to facilitate debugging and error-removing. The next section of this article describes how to build a Java thread switch monitoring agent based on the JVMTI interface.

Back to top of page

Java Threading Model

To monitor the switching of Java threads, you must first understand the Java threading model in the JVM. The Java threading model can be described in the Java thread life cycle shown in Figure 2. The life cycle of a Java thread consists of creating, ready, running, blocking, and dying 5 states. A Java thread is always in one of these 5 lifecycle States and can be converted between different states under certain conditions.

Figure 2. Java Threading Model
    • Create state (New Thread)
      When a thread is created using the new operator in the Java language, the thread is simply an empty object that has some characteristics of the thread, but at this point the system does not allocate resources for it, and the thread is in the created state.
    • Ready state (Runnable)
      When a thread is started using the start () method, the system allocates the required resources, except the CPU, to the thread in a ready state. In addition, if a thread executes the yield () method, the thread is temporarily stripped of the CPU resources and re-entered the ready state.
    • Operational status (Running)
      The Java runtime selects a thread in the ready state by scheduling it to occupy the CPU and turn it into a running state. At this point, the system actually executes the thread's run () method.
    • Blocking state (Blocked)
      When a running thread cannot continue running for some reason, it goes into a blocking state. These reasons include: when a method of blocking type such as suspend (), sleep () of a thread object is executed, the thread object is placed into a block set (Blocked Pool), waits for wake (execution of the resume () method), or automatically wakes up when it is over time. When multiple threads attempt to enter a synchronization region (synchronized), a thread that fails to enter the synchronization area is placed in the lock Pool until the lock of that synchronization region is acquired, and when the thread executes the wait () method of an object, The thread is placed in the waiting set (wait Pool) of the object until the Notify () method of the object is executed, and the execution of the Wait ()/notify () method requires the thread to first obtain a lock on the object.
    • Death Status (Dead)
      The thread enters the dead state after the run () method finishes executing. In addition, if the thread executes the interrupt () or stop () method, it will also enter the dead state in an abnormally exited manner.

Back to top of page

Context information for Java thread switching

Through the analysis of the Java threading Model, the context information involved in Java thread switching can be divided into explicit thread switching information and implicit thread switching information. Monitoring of Java thread switching can be achieved by capturing context information for thread switching through the JVMTI interface during Java thread switching.

  • Explicit thread-Switching information
    Explicit thread switching refers to a thread object that explicitly operates on another thread object (calling a thread method), which transforms the state of the target thread, and its contextual information can be described by using triples < operations threads, actions, and actions threads >. For example, the startup context of a Java thread can be described as <thread-caller, start, thread-callee>. From the Java threading model, the Java thread is created by deriving the thread class or the Runnable interface, and starting the newly established thread object by invoking the start () method in the thread class, so by monitoring the call to the start () method and combining the The analysis of the Java method call stack allows you to analyze the context information of the thread startup. According to the Java threading model, explicit thread switching involves a number of threading methods such as Start (), Interrupt (), join (), resume (), suspend (), sleep (), yield (), and stop ().
  • Implicit thread-Switching information
    In some cases, the switching of threads is not done by calling thread methods directly between threads, but by communicating between threads. This is the contextual information generated by the inter-thread communication, which is called implicit thread switching information. A typical example is the Java thread pool. Typically, there are several pre-created threads stored in the thread pool that are blocked and do not handle the task, and when a task arrives, the thread that is responsible for scheduling the task wakes up a blocked idle thread in the thread pool to handle the task, and once the task is completed, the thread that executes the task is not undone. Instead, it continues to be in a blocking state and is reset back to the thread pool, waiting for the next dispatch. Although the Java thread pool is implemented differently, its essence is implemented through the thread Communication primitives wait () and notify ()/notifyall () Methods of the Object class in Java. The wait () method causes the thread to wait on the specified target, while the Notify () method wakes up a thread waiting on the specified target, and the specific thread that actually wakes up depends on how the virtual machine is implemented, and the Notifyall () method wakes up all the threads. Implicit thread switching information can also be described using triples < operations threads, actions, and Threads >, but the establishment of this ternary group is more complex than explicit thread switching and needs to be obtained by analyzing the context information of the thread communication primitives execution.

The next section describes how to get context information for thread switching.

Back to top of page

Monitoring model for Java thread switching

Java Threading Monitoring Model 3 is designed according to the Java thread switching mechanism. The thread monitoring proxy agent registers an event listener between the method caller and the callee through JVMTI to listen for thread switching events (that is, method calls that cause thread switching). When a thread method is about to be called, the listener sends the method into the event (method Entry) to the agent's before Advice callback method; When a thread method returns, the listener sends the method Exit event to the agent's After Advice callback method. Before Advice and after Advice are respectively responsible for processing the thread state before the thread method call and the thread state after the thread method call. This allows the thread monitoring agent to monitor the state switching of Java threads.

Figure 3. Java Threading Monitor Model

Gets the context information for an explicit thread switch

When a Java method call occurs, it is easy to get the method name and thread identity that executes the method through the JVMTI interface. Therefore, the key to determining the thread switching context triples < The action thread, the action, and the thread > being manipulated is how to get the manipulated thread object identity, which requires parsing the calling mechanism of the Java method.

Each time a new thread is started, the Java Virtual Opportunity assigns it a Java stack. The Java stack holds the running state of the thread as a frame (frame). The virtual machine performs only two operations on the Java stack: in-frame and out-of-stack.

When a thread invokes a Java method, the virtual opportunity presses a new stack frame in the Java stack where the thread resides, storing the address of the Java method, which is called the current method, and the stack frame is the current stack frame. The stack frame is usually composed of a local variable area, an operand stack, and a frame data area. When executing the current method, it uses the current stack frame to store data such as parameters, local variables, intermediate operation results, and so on. The stack frame is created at the time of the method call and is destroyed when the method is completed.

Further research on stack frames found that when an instance method of an object executes, the Java virtual opportunity implicitly adds a reference to the object in the local variable area of the method's current stack frame. Although this parameter is not explicitly declared in the method source code, this parameter is implicitly joined by the JVM for any one instance method, and it is always the first in the local variable area. The local variable area is addressed according to the index, and the first local variable has an index of 0, so you can access the object reference by using the local variable area index 0, as shown in table 1.

Table 1. Local variable Area
Index Parameters
0 Object reference
1 Local Variables 1
2 Local Variables 2
...... ......

As a result, when the thread object executes a thread method, it can obtain a reference to the manipulated thread object by parsing the local method area of the current stack frame of the current operating thread. In this way, it is possible to fully determine the ternary group < operation threads, actions, and Operations threads > information for explicit thread switching.

It is important to note that there are threading methods, such as the sleep () and yield () methods, which are implemented as local methods (Native method), which do not generate the current method frame in the Java stack during invocation, but instead store the information on the local method stack (Native Stack). Therefore, thread switching caused by these methods cannot directly take the above analysis method, but should analyze the local method stack.

Gets the contextual information for an implicit thread switch

Similarly, the key to obtaining implicit thread-switching context information is to identify the ternary < operation thread, the action, and the manipulated thread object identified in the action thread >, which requires an analysis of the Java thread's synchronization mechanism.

Java uses a synchronization mechanism called Monitor to tune activity and share data for multiple threads, as shown in 4. In Java, each object corresponds to a single monitor, and even in multi-threaded cases, the area monitored by the monitor is only executed by one thread at a time. The only way a thread wants to execute the code for the monitoring area is to obtain the corresponding monitor for that region. When a thread reaches the beginning of a monitoring area (Entry point), it is placed in the monitor's request set (Entry Pool). If no other thread is waiting in the request set at this time, and no other line is impersonating holds the monitor (monitor hold), then the thread can get the monitor and continue executing the code in the monitoring area. When the thread finishes executing the monitoring area code, it exits and releases the monitor (Release monitor). If the thread executes the wait () method during the execution of the Monitoring area code, the thread temporarily frees the monitor and is placed in the wait set (wait Pool), waiting to be awakened, if the thread executes the Notify () method in the process. Then a thread in the waiting set is flagged as waking up, and the waking thread acquires the monitor at a time when the monitor is idle and executes the monitoring area code, depending on which thread is awakened depending on how the virtual machine is implemented. The Notifyall () method marks all threads in the wait set as waking up and accepts the scheduling of the virtual machine. A thread can execute the Wait () method only if it already owns a monitor, and it can only leave the wait set by becoming the holder of the monitor again.

Figure 4. Synchronization mechanism for Java threads

Based on the above analysis of the Java thread synchronization mechanism, if it is possible to get a snapshot of the state of its monitor wait set and compare it before and after executing the Wait ()/notify ()/notifyall () method, it can be concluded which or which thread is being woken up. These threads are the manipulated threads for this implicit thread switchover. In this way, you can determine the thread switching of the triples < operations thread, the action, the thread > information being manipulated.

In addition, because a thread may have multiple object monitors at the same time, the wait set on each monitor must be parsed to determine the monitor that is actually performing the Wait ()/notify ()/notifyall () method that is currently executing.

Back to top of page

Implementing a Java Thread monitoring agent

This section describes how to implement a Java Thread Monitoring agent (agent) based on the Java thread switching monitoring model.

The thread monitoring agent is a JVMTI-based C language implementation that captures thread-switching information from a running Java application from an instance of a virtual runtime without affecting the program's operation.

Initializing the monitoring agent

First, the monitoring agent must include a JVMTI header file, and the main code snippet is shown in Listing 1. The header file contains some definitions that JVMTI must have, usually in the include directory of the JDK installation directory.

Second, the agent needs to be initialized, and the work is done by the Agent_onload () method. This method is primarily used to set the required functionality (capabilities), register event Notification, and specify the event callback function (Callback method) before initializing the Java virtual machine. where the GetEnv () method is used to obtain the current virtual machine runtime environment; the Addcapabilities () method is used to set the functionality required by the thread monitoring agent, including method invocation and return events, access method local variables, get object or thread-owned monitor information The Seteventnotificationmode () method is used to capture the invocation of each instance method and the returned event notification; the Seteventcallbacks () method is used to register the appropriate method to enter the callback function and the method exit callback function.

Listing 1. Initialize Agent
Include "jvmti.h" Jniexport jint jnicall agent_onload (JAVAVM *jvm, char *options, void *reserved) {     jvm->getenv ( void * *) &GB_JVMTI, jvmti_version_1_0);     gb_capa.can_generate_method_exit_events = 1;     Gb_capa.can_access_local_variables=1;     gb_capa.can_get_monitor_info=1;     Gb_jvmti->addcapabilities (&gb_capa);     Callbacks. Methodentry = &callbackMethodEntry;     Callbacks. Methodexit = &callbackMethodExit;     Gb_jvmti->seteventcallbacks (&callbacks, sizeof (callbacks));     Gb_jvmti->seteventnotificationmode (jvmti_enable,jvmti_event_method_entry,null);     Gb_jvmti->seteventnotificationmode (jvmti_enable,jvmti_event_method_exit,null);     return JNI_OK; }
Monitoring explicit thread Switching

The monitoring of explicit thread switching is done through the Callbackmethodentry () callback method, and the main code snippet is shown in Listing 2. The callback method uses the Rawmonitorentry () and Rawmonitorexit () methods to set the monitoring area of the original monitor to monitor the threading methods that cause explicit thread switching, such as Start (), Interrupt (), join (), resume (), and so on.

When one of the above thread methods is about to be called, the hash value of the current operation thread is computed by using the Getobjecthashcode () method, which uniquely identifies the thread object, and then uses the Getlocalobject () method to obtain an object reference to the current method frame in the Java stack of the operation thread. This reference points to the manipulated thread object. This allows you to get context information for an explicit thread switch, which can be described as: <thread-a, Start, thread-b>, <thread-b, resume, thread-c>, and so on.

Listing 2. Monitoring explicit thread Switching
static void Jnicall Callbackmethodentry (jvmtienv *jvmti_env, jnienv* env, Jthread thr, Jmethodid method) {     gb_jvmti-& Gt Rawmonitorenter (gb_lock);     if (strcmp (name, "Start") ==0| | strcmp (name, "Interrupt") ==0| |         strcmp (name, "join") ==0| | strcmp (name, "Stop") ==0| |         strcmp (name, "suspend") ==0| | strcmp (name, "Resume") ==0) {         gb_jvmti->getlocalobject (thr,0,0,&thd_ptr);         Gb_jvmti->getobjecthashcode (Thd_ptr, &hash_code);     }     Gb_jvmti->rawmonitorexit (Gb_lock); }
Monitoring implicit thread switching

The monitoring of implicit thread switching is implemented by capturing the wait set information before and after the Wait ()/notify ()/notifyall () method call.

Take the Notify () method as an example, when the Notify () method is about to be called, first use the Getownedmonitorinfo () method in the Callbackmethodentry () method to get the monitor owned by the current operating thread, and then use the The Getobjectmonitorusage () method gets the thread objects that are waiting on each monitor and saves them in the implicit thread-switching information chain to wait for analysis, as shown in Listing 3.

Listing 3. Gets the wait set before the Notify method call
static void Jnicall Callbackmethodentry (jvmtienv *jvmti_env, jnienv* env, Jthread thr, Jmethodid method) {     if (strcmp ( Name, "notify") ==0| | strcmp (name, "Notifyall") ==0) {         Gb_jvmti->getownedmonitorinfo (thr,&owned_monitor_count,&owned_ MONITORS_PTR);         for (int index=0;index<owned_monitor_count;index++) {             jvmtimonitorusage *info_ptr=null;             Info_ptr= (jvmtimonitorusage*) malloc (sizeof (jvmtimonitorusage));             Gb_jvmti->getobjectmonitorusage (* (Owned_monitors_ptr+index), info_ptr);             Insertelem (&inf_head,&inf_tail,info_ptr);         }     } }

When the Notify () method is about to complete the call, in the Callbackmethodexit () method, it also acquires the current operation thread's wait set (Notify_waiters) and compares it with the waiting set previously recorded, where the object with the difference is the manipulated thread object. This allows you to determine the contextual information for the implicit thread switch, which can be described as: <thread-a, notify, thread-b>. The main code snippet is shown in Listing 4.

Listing 4. Parsing the wait set before and after the Notify method call
static void Jnicall Callbackmethodexit (jvmtienv *jvmti_env, jnienv* env, Jthread thr, Jmethodid method) {     //compare th E-Wait Pools     if (info_ptr->notify_waiter_count!=inf_head->info_ptr->notify_waiter_count) {         for (int i=0;i<inf_head->info_ptr->notify_waiter_count;i++) {for             (int j=0;j<info_ptr->notify_waiter_count;j++) {                 if (inf_head->info_ptr->notify_waiters+i !=INFO_PTR->NOTIFY_WAITERS+J) {                     isobj=false;                     break;                 }             }             if (isobj==true) {                 getobjecthashcode (* (inf_head->info_ptr->notify_waiters+i), &hash_code);                 Insertelem (&ctx_head,&ctx_tail,thr_hash_code,hash_code,name);}}}     }

Back to top of page

Test Monitoring Agent

The thread monitoring agent can be compiled with any C language compiler and loaded into a Java virtual machine as a dynamic connection library to monitor running Java programs. Use the following command line to load the agent library and run the target Java application:

Java-agentlib:threadmonitor Javathreadpoolapp

Or:

Java-agentpath:/<path>/threadmonitor Javathreadpoolapp

To demonstrate monitoring of thread switching, Javathreadpoolapp This sample program implements a Java thread pool that involves a large number of thread state transitions. The application initiates a thread pool, initializes two sub-threads in the pool, and keeps them in the wait state, and when a client program needs to use one of the threads in the thread pool, it wakes one of the threads in the pool using notify, and when finished, the thread re-enters the wait state to wait for the next dispatch. These thread switching activities can be monitored by the monitoring agent and produce the following output. Combined with information about the application, you can further conclude that the Javathreadpoolapp client application uses the Thread-60934352 thread in the thread pool and executes for 3068 milliseconds.

Thread-32452561, start, Thread-60934352 Thread-32452561, start, Thread-89877242 Thread-60934352, wait, Thread-60934352 , active 8 Ms Thread-89877242, wait, Thread-89877242, active 9 Ms Thread-32452561, notify, Thread-60934352 Thread-60934352 , wait, Thread-60934352, active 3068 ms

The thread monitoring agent enables real-time monitoring of the JVM's threads, helping developers diagnose thread scheduling problems that may exist in multithreaded applications.

Back to top of page

Summarize

There are many challenges in the development of multithreaded applications, such as thread deadlock, resource competition, and so on. As a result, developers need a run-time thread monitoring tool to assist in diagnosing and tracking the switching of Java thread state. In JDK 1.5.0, Sun introduced the JVMTI interface for real-time monitoring and analysis of the JVM's operational status.

In this paper, the JVMTI interface is introduced firstly, then the Java threading Model is analyzed in detail, and the Java Threading Monitoring model is presented, and the Java Thread Monitoring agent is implemented based on the JVMTI interface to assist in diagnosing the possible thread scheduling problems in multi-threaded applications.

Back to top of page

Download
Description name size
Sample code Tmondemo913.zip 535 KB

http://www.ibm.com/developerworks/cn/java/j-lo-jvmti/

Implementation of Java thread monitoring based on JVMTI (RPM)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.