`
Josh_Persistence
  • 浏览: 1651619 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

JVM性能调优监控工具专题二:VisualVM基本篇之监控JVM内存,CPU,线程

阅读更多

前言:

        上一个专题中讲述了JVM中自带的各种性能测试的小工具:包括jps,jstatck,jmap,jhat,jsats,hprof

http://josh-persistence.iteye.com/blog/2161848,根据项目应用中的具体情况,如果想要查看Java进程中线程堆栈的信息,可以选择jstack,如果要查看堆内存,可以使用jmap导出并使用jhat来进行分析,包括查看类的加载信息,GC算法那,对象的使用情况等,还可以使用jstat来对JVM进行统计监测,包括查看各个区内存和GC的情况,还可以使用hprof能够展现CPU使用率,统计堆内存使用情况。

        这样会造成不必要的麻烦,难道就没有一个tool可以 包括如上所有的功能?答案是有的,自从 JDK 6 Update 7以后,提供了一全新的性能检测工具:VisualVM,VisualVM对运行中的Java应用提供了可视化的信息展示, 它是很多工具的整合包,整合了JConsole,jstat,jinfo,jstack以及jmap。

 

下一个专题将讲解VirualVM对GC的监控,以及远程监控,对Tomcat、Jboss等的监控以及在Eclipse中集成 VirualVM

 

二、基本概述:

        VisualVM 是一款免费的\集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高性能分析的精度。

       开发大型 Java 应用程序的过程中难免遇到内存泄露、性能瓶颈等问题,比如文件、网络、数据库的连接未释放,未优化的算法等。随着应用程序的持续运行,可能会造成整个系统运行效率下降,严重的则会造成系统崩溃。为了找出程序中隐藏的这些问题,在项目开发后期往往会使用性能分析工具来对应用程序的性能进行分析和优化。

 

三、背景知识

性能分析的主要方式

  • 监视:监视是一种用来查看应用程序运行时行为的一般方法。通常会有多个视图(View)分别实时地显示 CPU 使用情况、内存使用情况、线程状态以及其他一些有用的信息,以便用户能很快地发现问题的关键所在。
  • 转储:性能分析工具从内存中获得当前状态数据并存储到文件用于静态的性能分析。Java 程序是通过在启动 Java 程序时添加适当的条件参数来触发转储操作的。它包括以下三种:
    • 系统转储:JVM 生成的本地系统的转储,又称作核心转储。一般的,系统转储数据量大,需要平台相关的工具去分析,如 Windows 上的 windbg 和 Linux 上的 gdb。
    • Java 转储:JVM 内部生成的格式化后的数据,包括线程信息,类的加载信息以及堆的统计数据。通常也用于检测死锁。
    • 堆转储:JVM 将所有对象的堆内容存储到文件。
  • 快照:应用程序启动后,性能分析工具开始收集各种运行时数据,其中一些数据直接显示在监视视图中,而另外大部分数据被保存在内部,直到用户要求获取快照,基于这些保存的数据的统计信息才被显示出来。快照包含了应用程序在一段时间内的执行信息,通常有 CPU 快照和内存快照两种类型。
    • CPU 快照:主要包含了应用程序中函数的调用关系及运行时间,这些信息通常可以在 CPU 快照视图中进行查看。
    • 内存快照:主要包含了内存的分配和使用情况、载入的所有类、存在的对象信息及对象间的引用关系等。这些信息通常可以在内存快照视图中进行查看。
    • 性能分析:性能分析是通过收集程序运行时的执行数据来帮助开发人员定位程序需要被优化的部分,从而提高程序的运行速度或是内存使用效率,主要有以下三个方面:
      • CPU 性能分析:CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。通常有 CPU 监视和 CPU 快照两种方式来显示 CPU 性能分析结果。
      • 内存性能分析:内存性能分析的主要目的是通过统计内存使用情况检测可能存在的内存泄露问题及确定优化内存使用的方向。通常有内存监视和内存快照两种方式来显示内存性能分析结果。
      • 线程性能分析:线程性能分析主要用于在多线程应用程序中确定内存的问题所在。一般包括线程的状态变化情况,死锁情况和某个线程在线程生命期内状态的分布情况等。

四、安装

自从 JDK 6 Update 7 以后已经作为 Oracle JDK 的一部分,位于 JDK 根目录的 bin 文件夹下,无需安装,直接运行即可,但如果需要使用更多的插件,或者是自己开发插件,则需要安装,后面将会讲到。

 

五、实践

1、内存分析

       VisualVM 通过检测 JVM 中加载的类和对象信息等帮助我们分析内存使用情况,我们可以通过 VisualVM 的监视标签和 Profiler 标签对应用程序进行内存分析。在监视标签内,我们可以看到实时的应用程序内存堆以及永久保留区域的使用情况。

 

1)内存堆Heap

首先我们来看内存堆Heap使用情况,如果已将成功安装JDK6 Update7以上的版本,并且配置了环境变量,

可以直接在命令行中使用jvisualvm.exe来启动:



 

 

我本机eclipse的进程在没有启动任何程序时在visualVM显示如下:



 

 写了一个小程序,该程序会在内存中不停的append字符串3亿次,导致其会占用较大的内存:

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
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;
	}

	

}

 

在运行前,从VirsualVM中可以看到相应的内存占用情况为:



 

执行上面的程序后,查看VisualVM中监视,内存堆的大小,堆内存变大了,



 

在程序运行结束之前, 点击”堆Dump“按钮, 等待一会儿,得到dump结果,可以看到一些Summary信息

点击Classes, 发现char[]所占用的内存是最大的



 

 双击它,可能会得到如下Instances结果(不同的JDK,不同的机器配置结果很可能不同)



 

 Instances是按Size由大到小排列的

 

      第一个就是最大的, 展开Field区域的 values,你会发现StringBuffer类型的 全局变量 tempOOM 占用内存特别大, 注意局部变量是无法通过 堆dump来得到分析结果的。另外,对于“堆 dump”来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。

 

2)永久保留区域PermGen

 

其次来看下永久保留区域PermGen使用情况

运行一段类加载的程序,代码如下:

 

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  josh_wang23@hotmail.com
 * 
 */
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);
			// 从指定的jar包中load LogFactory并构造一个LogFactory的对象
			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指定了getLog传入String参数
			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;
	}

}
 

 

 一个类型装载之后会创建一个对应的java.lang.Class实例,这个实例本身和普通对象实例一样存储于堆中,我觉得之所以说是这是一种特殊的实例,某种程度上是因为其充当了访问PermGen区域中类型信息的代理者。

 

运行一段时间后抛OutOfMemoryError了, VisualVM监控结果如下:

 

  

结论:PermGen区域分配的堆空间过小,我们可以通过设置-XX: PermSize参数和-XX:MaxPermSize参数来解决,修改eclipse.ini中的内容或者是以jar包形式启动时加上类似-XX: PermSize32m -XX:MaxPermSize 512m 即: java -jar test.jar -Xms32m -Xmx512m

关于如何设置XX:PermSize,参照文章:http://blog.csdn.net/superbeck/article/details/4799407 

和这篇文章:http://stackoverflow.com/questions/14865647/launcher-xxmaxpermsize-appears-twice-in-eclipse-ini

关于PermGen OOM深入分析请参考这篇文章

关于Perform GC, 请参考这篇文章

 

 

CPU分析篇

CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。

没有程序运行时的 CPU 使用情况如下图:



 

 

运行一段 占用CPU 的小程序,代码如下

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
public class MemoryCPUTest {
	
	public static void main(String[] args) throws InterruptedException {
		cpuFix();
	}
	
	
	private static void cpuFix() throws InterruptedException {
		// 80%的占有率
		int busyTime = 8;
		
		// 20%的占有率
		int idelTime = 2;
		
		// 开始时间
		long startTime = 0;
		
		while (true) {
			startTime = System.currentTimeMillis();
			
			// 运行时间
			while (System.currentTimeMillis() - startTime < busyTime) {
				;
			}
			
			// 休息时间
			Thread.sleep(idelTime);
		}
	}

}

 

查看监视页面"监视“



 

 

过高的 CPU 使用率可能是由于我们的项目中存在低效的代码;

在我们对程序施压的时候,过低的 CPU 使用率也有可能是程序的问题。

 

 

点击抽样器, 点击“CPU”按钮, 启动CPU性能分析会话,VisualVM 会检测应用程序所有的被调用的方法,

 

在CPU样例tab 下可以看到我们的方法cpufix() 的自用时间最长, 如下图:



 

切换到线程CPU时间页面下,我们得main方法这个线程所占用的CPU时间最长,如下图:




 
 

线程分析篇

Java 语言能够很好的实现多线程应用程序。当我们对一个多线程应用程序进行调试或者开发后期做性能调优的时候,往往需要了解当前程序中所有线程的运行状态,是否有死锁、热锁等情况的发生,从而分析系统可能存在的问题。

在 VisualVM 的监视标签内,我们可以查看当前应用程序中所有活动线程(Live threads)和守护线程(Daemon threads)的数量等实时信息。

 

 运行一段小程序,代码如下:

 

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
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) {
			
		}
	}

}

 

 

 从下面的截图中可以看到,实时线程(Live Threads)和守护线程(Daemon Threads)都增加了。

 

 

 

VisualVM 的线程标签提供了三种视图,默认会以时间线的方式展现, 如下图:

可以看到两个我们run的程序里启的线程:BasicThreadTest-1 和 BasicThread-2



 

写一段简单的死锁程序,看看VisualVM能否检测出来

 

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

/**
 * @author Josh Wang(Sheng)
 * 
 * @email  josh_wang23@hotmail.com
 * 
 */
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 ++;
			}
		}
	}

}

 

 

打开相应的线程Tab,我们可以看到这个tab在闪,VisualVM已经检测到我这个package下面的VisualVMDeadLock类出错了,”检测到死锁“



 

点击”线程Dump按钮“,可以看到死锁的详细信息:



 

 

 

 

 

5
1
分享到:
评论
1 楼 jetable 2015-08-08  
好文章,谢谢博主的分享,收下了

相关推荐

    JAVA JVM性能调优监控工具详解

    ### JAVA JVM性能调优监控工具详解 在Java开发过程中,特别是在企业级应用中,经常会遇到各种性能瓶颈问题,如内存溢出(`OutOfMemoryError`)、内存泄露、线程死锁、锁争用等问题。这些问题如果不能及时有效地解决...

    深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理).zip

    这些工具可以实时监控JVM的内存状态、CPU使用率、线程情况,帮助定位性能瓶颈。 总的来说,深入理解Java虚拟机对于开发高性能、稳定的Java应用至关重要。通过学习JVM性能调优,我们可以优化应用程序的资源利用,...

    个人总结之—JVM性能调优实战

    - **性能瓶颈定位**:利用各种性能监控工具定位系统中的性能瓶颈,例如CPU使用率过高、内存占用过大等问题。 - 使用VisualVM进行CPU和内存监控; - 使用JMeter进行负载测试定位性能瓶颈。 ##### 4. 实战案例分析 ...

    JVM 监控 调优 工具

    2. **VisualVM**:功能更为强大的多合一JVM分析工具,提供CPU和内存分析、线程快照、方法Profiling等功能。 3. **JMX(Java Management Extensions)**:提供了一种标准的方式来管理和监控Java应用程序,可以通过...

    JVM性能调优经典教程

    6. **监控与诊断工具**:JDK自带的JConsole、VisualVM、JProfiler等工具可以帮助我们实时监控JVM状态,分析CPU、内存、线程等问题,找出性能瓶颈。 7. **JVM内存泄漏检测**:学习如何识别和处理内存泄漏问题,了解...

    JVM参数调优及JAVA相关工具使用

    Java相关工具,如JConsole、VisualVM、JProfiler等,可以帮助监控和分析JVM的运行状态,包括内存使用、垃圾收集、线程和CPU负载等,从而辅助进行调优工作。 为了优化JVM性能,还可以调整其他参数,如-Xss控制线程栈...

    jvm性能调优-jvm内存模型和优化-performance-jvm-memorymodel-optimize.zip

    3. **VisualVM**:集成了多种JDK自带工具,提供丰富的性能监控和诊断功能。 五、总结 JVM内存模型和优化是Java性能调优的核心部分,理解并熟练掌握这些知识,能够帮助开发者解决实际问题,提升系统的运行效率。...

    JVM性能调优

    ### JVM性能调优 #### JVM概念与发展历程 Java虚拟机(JVM)是Java Virtual Machine的简称,它是一种能够执行Java字节码的虚拟机。JVM的主要作用是在不同的平台上运行Java程序,使得Java程序具备跨平台的能力。...

    深入理解Java虚拟机视频教程(jvm性能调优+内存模型+虚拟机原理)视频教程

    第5节jvm再体验-jvm可视化监控工具 [免费观看] 00:21:17分钟 | 第6节杂谈 [免费观看] 00:12:37分钟 | 第7节Java的发展历史00:27:24分钟 | 第8节Java的发展历史续00:02:27分钟 | 第9节Java技术体系00:08:46分钟 |...

    visualvm 教程 介绍 性能调优

    VisualVM作为一款全面的性能分析工具,不仅简化了性能监控的过程,而且通过丰富的数据展示和分析功能,帮助开发者深入理解应用程序的运行状况,从而进行有效的性能调优。无论是在开发阶段的性能测试,还是在生产环境...

    jvm监控工具visualVM

    VisualVM是一款强大的JVM(Java Virtual Machine)监控和性能分析工具,由Oracle公司开发并提供。它集成在Java SDK中,默认位于%JAVA...无论是调试、性能调优还是日常监控,VisualVM都是Java开发者不可或缺的工具之一。

    visualvm监控jvm及远程jvm监控方法

    - **性能监控**:在"Monitors"选项卡中,你可以查看CPU使用率、内存分配、线程状态等关键信息。 - **堆内存分析**:"Memory"选项卡用于查看堆内存使用情况,包括对象分配、GC活动和内存泄漏检测。 - **类加载**:...

    Java高并发编程与JVM性能调优实战 视频教程 下载下载因为太大存百度云盘2.zip

    4. **JVM性能监控与诊断工具**:如JConsole、VisualVM、JProfiler等工具的使用,以及如何通过JMX进行远程监控。 5. **JVM参数调优实践**:如何设置Xms、Xmx、XX:NewRatio、XX:SurvivorRatio、XX:MaxHeapFreeRatio等...

    Java高并发编程与JVM性能调优实战 视频教程 下载下载因为太大存百度云盘4.zip

    5. **JVM调优工具**:学习使用JDK自带的JConsole、VisualVM、JFR等工具进行性能监控和分析,以及如何通过JVM日志分析系统瓶颈。 6. **类加载机制**:理解类加载过程(加载、验证、准备、解析、初始化),探讨双亲...

    JVM调优工具分享

    总的来说,JConsole和VisualVM都是强大的JVM监控工具,它们提供了丰富的功能来协助开发者进行性能优化。JConsole简洁易用,适合日常的基本监控;而VisualVM则更为全面,尤其在插件的支持下,可以进行深入的性能分析...

    jvm工具、参数调优&调试技巧

    1. JConsole:这是Java自带的一款图形化监控工具,可以实时监控JVM的内存、线程、类加载等状态,辅助开发者分析和诊断问题。 2. JVisualVM:它提供了一个全面的视图,包括性能监视、线程分析、内存检测等功能,同时...

    java -jvm 内存分配和jvm调优

    10. 监控与诊断工具:如VisualVM、JConsole、JMX等可以帮助我们实时监控JVM状态,找出性能瓶颈。 总结,Java JVM内存分配和调优是一项复杂的任务,需要结合实际应用的需求和性能指标来调整。通过理解JVM内存模型,...

    jvm参数调优-jvmSample.zip

    三、JVM性能监控与调优工具 1. JConsole:提供实时的JVM内存、线程、类加载等监控信息。 2. VisualVM:集成了多种性能分析工具,包括内存分析、线程分析、CPU分析等。 3. JProfiler:强大的商业级JVM分析工具,提供...

    深入JVM和调优+高性能并发Web服务器实现核心内幕

    6. **性能分析与优化工具**:除了前面提到的VisualVM和JConsole,JProfiler、YourKit等也是强大的JVM性能分析工具,它们可以帮助开发者找到CPU、内存、线程等方面的瓶颈。 7. **JVM内存模型**:理解JMM(Java内存...

    可视化性能监控工具VisualVM

    总的来说,VisualVM作为一款免费且功能全面的Java性能分析工具,为开发者提供了强大的性能监控和问题排查手段。通过深入理解VisualVM的使用,开发者能够更好地优化代码,提升Java应用程序的性能和稳定性。

Global site tag (gtag.js) - Google Analytics