JVM Performance Tuning Monitoring Tools Topic 2: VisualVM Basics Monitoring JVM Memory, CPU, Threads

Foreword:

        The previous topic described various performance testing tools that come with the JVM: including jps, jstatck, jmap, jhat, jsats, hprof

http://josh-persistence.iteye.com/blog/2161848, according to the specific situation in the project application, if you want to view the thread stack information in the Java process, you can choose jstack, if you want to view the heap memory, you can use jmap to export And use jhat for analysis, including viewing class loading information, GC algorithm, object usage, etc. You can also use jstat to perform statistical monitoring on the JVM, including viewing the memory and GC of each area, you can also use hprof to Display CPU usage and statistics heap memory usage.

        This will cause unnecessary trouble, isn't there a tool that can include all the above functions? The answer is yes, since JDK 6 Update 7, a brand new performance detection tool has been provided: VisualVM, VisualVM provides visual information display for running Java applications, it is an integration package of many tools, and integrates JConsole, jstat , jinfo, jstack and jmap.

 

The next topic will explain the monitoring of GC by VirtualVM, as well as remote monitoring, monitoring of Tomcat, Jboss, etc., and the integration of  VirtualVM in Eclipse

 

2. Basic overview:

        VisualVM is a free visualization tool that integrates multiple JDK command-line tools. It can provide you with powerful analysis capabilities for performance analysis and tuning of Java applications. These capabilities include generating and analyzing massive amounts of data, tracking memory leaks, monitoring the garbage collector, performing memory and CPU analysis, and it also supports browsing and manipulation on MBeans. It obtains real-time data from the program runtime through jvmstat, JMX, SA (Serviceability Agent) and Attach API, etc., so as to perform dynamic performance analysis. At the same time, it can automatically select faster and lighter technology to minimize the impact of performance analysis on the application and improve the accuracy of performance analysis.

       In the process of developing large-scale Java applications, it is inevitable to encounter problems such as memory leaks and performance bottlenecks, such as unreleased connections to files, networks, and databases, and unoptimized algorithms. With the continuous operation of the application, the operating efficiency of the entire system may decrease, and in severe cases, the system may crash. In order to find these hidden problems in the program, performance analysis tools are often used in the later stage of project development to analyze and optimize the performance of the application.

 

3. Background knowledge

The main way of performance analysis

  • Monitoring: Monitoring is a general method used to view the runtime behavior of an application. There are usually multiple views (View) that display CPU usage, memory usage, thread status, and other useful information in real time, so that users can quickly find the crux of the problem.
  • Dump: The performance analysis tool obtains the current state data from memory and stores it to a file for static performance analysis. A Java program triggers a dump operation by adding the appropriate conditional parameters when the Java program is started . It includes the following three:
    • System Dump: A dump of the local system generated by the JVM, also known as a core dump. Generally, system dump data is large and requires platform-related tools to analyze, such as windbg on Windows and gdb on Linux.
    • Java dump: Formatted data internally generated by the JVM, including thread information, class loading information, and heap statistics. Also commonly used to detect deadlocks.
    • Heap Dump: The JVM stores the heap contents of all objects to a file.
  • Snapshots: After the application starts, performance analysis tools begin to collect various runtime data, some of which are displayed directly in the monitoring view, while most of the data is kept internally until the user requests a snapshot , based on these saved data. Statistics are displayed. Snapshots contain the execution information of an application over a period of time. There are usually two types: CPU snapshots and memory snapshots.
    • CPU snapshot: It mainly includes the calling relationship and running time of functions in the application, and this information can usually be viewed in the CPU snapshot view.
    • Memory snapshot: It mainly includes memory allocation and usage, all classes loaded, existing object information, and reference relationships between objects. This information can usually be viewed in the memory snapshot view.
    • Performance analysis: Performance analysis is to help developers locate the parts of the program that need to be optimized by collecting the execution data when the program is running, so as to improve the running speed of the program or the efficiency of memory usage, mainly in the following three aspects:
      • CPU performance analysis: The main purpose of CPU performance analysis is to count the invocation and execution time of functions, or more simply, the CPU usage of the application. There are usually two ways to display CPU performance analysis results: CPU monitoring and CPU snapshot.
      • Memory performance analysis: The main purpose of memory performance analysis is to detect possible memory leaks through statistical memory usage and to determine the direction of optimizing memory usage. There are usually two ways to display memory performance analysis results: memory monitoring and memory snapshots.
      • Thread profiling: Thread profiling is primarily used to identify memory problems in multithreaded applications. Generally, it includes the state change of the thread, the deadlock situation and the distribution of the state of a thread in the thread lifetime.

4. Installation

Since JDK 6 Update 7, it has been used as a part of Oracle JDK and is located in the bin folder of the JDK root directory. It does not need to be installed and can be run directly. However, if you need to use more plug-ins, or develop your own plug-ins, you need to install them. It will be mentioned later.

 

5. Practice

1. Memory analysis

       VisualVM helps us analyze memory usage by detecting the class and object information loaded in the JVM, and we can perform memory analysis on the application through VisualVM's monitoring tab and Profiler tab. In the monitoring tab, we can see the real-time application memory heap and the usage of the permanent reserved area.

 

1) Memory heap Heap

First, let's look at the usage of the memory heap. If the version above JDK6 Update7 has been successfully installed, and the environment variables have been configured,

You can use jvisualvm.exe directly from the command line to start:



 

 

The process of my native eclipse shows the following in visualVM when no program is started:



 

 I wrote a small program that will append the string 300 million times in memory, causing it to take up a lot of memory:

package com.wsheng.aggregator.thread.performance.visualvm;

/**
 * @author Josh Wang(Sheng)
 *
 * @email  [email protected]
 *
 */
public class HeapMemoryTest {
	
	private static final int OUTOFMEMORY = 300000000;
	
	private String oom;
	
	private int length;
	
	
	StringBuffer tempOOM = new StringBuffer();
	
	public HeapMemoryTest(int length) {
		this.length = length;
		
		int i = 0;
		
		while (i < length) {
			i ++;
			try {
				tempOOM.append("a");
			} catch (Exception e) {
				e.printStackTrace ();
				break;
			}
		}
		this.oom = tempOOM.toString();
	}
	
	public static void main(String[] args) throws Exception {
		HeapMemoryTest heapMemoryTest = new HeapMemoryTest(OUTOFMEMORY);
		Thread.sleep(5000);
		System.out.println(heapMemoryTest.getOom().length());
	}

	public String getOom() {
		return oom;
	}

	public void setOom(String oom) {
		this.oom = oom;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	

}

 

Before running, the corresponding memory usage can be seen from VirtualVM as follows:



 

After executing the above program, check the monitoring in VisualVM, the size of the memory heap, the heap memory has become larger,



 

Before the program ends, click the "Heap Dump" button, wait for a while, get the dump result, and you can see some summary information

Click on Classes and find that the memory occupied by char[] is the largest



 

 Double-click it, you may get the following Instances results (different JDK, different machine configuration results are likely to be different)



 

 Instances are arranged in descending order of Size

 

      The first one is the largest. Expand the values ​​in the Field area, and you will find that the global variable tempOOM of StringBuffer type occupies a large amount of memory. Note that local variables cannot be analyzed by heap dump. In addition, for "heap dump", VisualVM does not have this function when monitoring jvm remotely, only when monitoring locally.

 

2) Permanent reservation area PermGen

 

Next, let's take a look at the usage of PermGen in the permanent reserved area

Run a class loaded program, the code is as follows:

 

package com.wsheng.aggregator.thread.performance.visualvm;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Josh Wang(Sheng)
 *
 * @email  [email protected]
 *
 */
public class PermGenTest {
	
	private static List<Object> logObjectList = new ArrayList<Object>();
	
	public static void main(String[] args) throws Exception {
		permLeak ();
	}
	
	private static void permLeak() throws Exception {
		for (int i = 0; i < 1000; i++) {
			URL[] urls = getUrls();
			URLClassLoader urlClassLoader = new URLClassLoader(urls, null);
			// Load LogFactory from the specified jar package and construct a LogFactory object
			Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true, urlClassLoader);
			Method getLog = logfClass.getMethod("getLog", String.class);
			Object result = getLog.invoke(logfClass, "PermGenTest"); // String.class specifies the incoming String parameter of getLog
			logObjectList.add(result);
			
			System.out.println(i + ": " + result);
		}
	}
	
	private static URL[] getUrls() throws MalformedURLException {
		File libDir = new File("C:/Users/wangsheng/.m2/repository/commons-logging/commons-logging/1.1.1");
		File[] subFiles = libDir.listFiles();
		int count = subFiles.length;
		URL[] urls = new URL[count];
		for (int i = 0; i < count; i++) {
			urls[i] = subFiles[i].toURI().toURL();
		}
		
		return urls;
	}

}
 

 

 After a type is loaded, a corresponding java.lang.Class instance will be created, which itself is stored in the heap like a normal object instance. I think the reason why this is a special instance is partly because of its Acts as a proxy for accessing type information in the PermGen area.

 

After running for a period of time, OutOfMemoryError is thrown, and the monitoring results of VisualVM are as follows:

 

  

Conclusion: The heap space allocated in the PermGen area is too small, we can solve it by setting the -XX: PermSize parameter and the -XX:MaxPermSize parameter, modify the content in eclipse.ini or add something like -XX when starting in the form of a jar package: PermSize 32m  -XX:MaxPermSize  512m ie: java -jar test.jar -Xms32m -Xmx512m 

For how to set XX:PermSize, refer to the article: http://blog.csdn.net/superbeck/article/details/4799407 

and this post: http://stackoverflow.com/questions/14865647/launcher-xxmaxpermsize-appears-twice-in-eclipse-ini

For in-depth analysis of PermGen OOM, please refer to this article

About Perform GC, please refer to this article

 

 

CPU Analysis

The main purpose of CPU performance analysis is to count the calling and execution time of functions, or more simply, the CPU usage of the application.

The CPU usage when no program is running is as follows:



 

 

Run a small program that occupies the CPU, the code is as follows

package com.wsheng.aggregator.thread.performance.visualvm;

/**
 * @author Josh Wang(Sheng)
 *
 * @email  [email protected]
 *
 */
public class MemoryCPUTest {
	
	public static void main(String[] args) throws InterruptedException {
		cpuFix();
	}
	
	
	private static void cpuFix() throws InterruptedException {
		// 80% share
		int busyTime = 8;
		
		// 20% share
		int idelTime = 2;
		
		// Starting time
		long startTime = 0;
		
		while (true) {
			startTime = System.currentTimeMillis();
			
			// operation hours
			while (System.currentTimeMillis() - startTime < busyTime) {
				;
			}
			
			// Break time
			Thread.sleep(idelTime);
		}
	}

}

 

View the monitoring page "Monitoring"



 

 

High CPU usage may be due to inefficient code in our project;

When we stress the program, too low CPU usage may also be a problem with the program.

 

 

Click the sampler, click the "CPU" button, start the CPU profiling session, VisualVM will detect all the called methods of the application,

 

Under the CPU sample tab, we can see that our method cpufix() has the longest self-use time, as shown in the following figure:



 

Switch to the thread CPU time page, we have to main method this thread occupies the longest CPU time, as shown below:




 
 

Thread Analysis

The Java language is well suited for implementing multithreaded applications. When we debug a multi-threaded application or perform performance tuning in the later stage of development, we often need to know the running status of all threads in the current program, whether there is deadlock, hot lock, etc., so as to analyze the possible existence of the system. question.

In the monitoring tab of VisualVM, we can view real-time information such as the number of all active threads (Live threads) and daemon threads (Daemon threads) in the current application.

 

 Run a small program, the code is as follows:

 

package com.wsheng.aggregator.thread.performance.visualvm;

/**
 * @author Josh Wang(Sheng)
 *
 * @email  [email protected]
 *
 */
public class BasicThreadTest extends Thread {
	
	public static void main(String[] args) {
		
		BasicThreadTest b1 = new BasicThreadTest("Thread a");
		BasicThreadTest b2 = new BasicThreadTest("Thread b");
		
		b1.setName("BasicThreadTest-1");
		b2.setName("BasicThreadTest-2");
		
		b1.start();
		b2.start();
	}
	
	public BasicThreadTest(String name) {
		
	}
	
	@Override
	public void run() {
		while (true) {
			
		}
	}

}

 

 

 As you can see from the screenshot below, both Live Threads and Daemon Threads have increased.

 

 

 

The thread tab of VisualVM provides three views, which are displayed in the timeline by default, as shown in the following figure:

You can see two threads started in the program we run: BasicThreadTest-1 and BasicThread-2



 

Write a simple deadlock program and see if VisualVM can detect it

 

package com.wsheng.aggregator.thread.performance.visualvm;

/**
 * @author Josh Wang(Sheng)
 *
 * @email  [email protected]
 *
 */
public class VisualVMDeadLock {
	
	public static void main(String[] args) {
		VisualVMDeadLock lock = new VisualVMDeadLock();
		Resource r1 = lock.new Resource();
		Resource r2 = lock.new Resource();
		
		Thread lockThread1 = lock.new LockThread1(r1, r2);
		Thread lockThread2 = lock.new LockThread2(r1, r2);
		
		lockThread1.setName("DeadLock-1");
		lockThread2.setName("DeadLock-2");
		
		lockThread1.start();
		lockThread2.start();
	}
	
	
	class Resource {
		private int i;

		public int getI() {
			return i;
		}

		public void setI(int i) {
			this.i = i;
		}
		
		
	}
	
	class LockThread1 extends Thread {
		private Resource r1, r2;
		
		public LockThread1(Resource r1, Resource r2) {
			this.r1 = r1;
			this.r2 = r2;
		}
		
		
		@Override
		public void run() {
			int j = 0;
			while (true) {
				synchronized (r1) {
					System.out.println("The first thread got r1's lock " + j);
					synchronized (r2) {
						System.out.println("The first thread got r2's lock " + j);
					}
				}
				j++;
			}
		}
	}
	
	class LockThread2 extends Thread {
		private Resource r1, r2;
		
		public LockThread2(Resource r1, Resource r2) {
			this.r1 = r1;
			this.r2 = r2;
		}
		
		@Override
		public void run() {
			int j = 0;
			while (true) {
				synchronized (r2) {
					System.out.println("The second thread got r2's lock " + j);
					synchronized (r1) {
						System.out.println("The second thread got r1's lock " + j);
					}
				}
				j ++;
			}
		}
	}

}

 

 

Open the corresponding thread Tab, we can see that this tab is flashing, VisualVM has detected an error in the VisualVMDeadLock class under my package, "Deadlock detected"



 

Click the "Thread Dump button" to see the details of the deadlock:



 

 

 

 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326988896&siteId=291194637