2019独角兽企业重金招聘Python工程师标准>>>

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

前言:

上一个专题中讲述了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) {}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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按钮“,可以看到死锁的详细信息:

http://itindex.net/detail/54099-jvm-%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98-%E5%B7%A5%E5%85%B7

http://www.codeweblog.com/%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98%E5%92%8C%E5%8E%8B%E6%B5%8B%E7%9C%8B%E6%87%82jvisualvm/

转载于:https://my.oschina.net/xiaominmin/blog/1597242

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

  1. JVM性能调优监控工具专题一:JVM自带性能调优工具(jps,jstack,jmap,jhat,jstat,hprof)...

    2019独角兽企业重金招聘Python工程师标准>>> 前提概要:         JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外 ...

  2. 5款强大的JVM 性能调优监控工具 !

    点击上方 好好学java ,选择 星标 公众号 重磅资讯,干货,第一时间送达 今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客 ...

  3. JVM性能调优监控工具使用详解

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 来源:http://uee.me/czpHk 现实企业级J ...

  4. linux打印jvm内存堆栈_5款强大的JVM 性能调优监控工具

    原文链接:www.iteye.com/blog/josh-persistence-2161848 现实企业级Java应用开发.维护中,有时候我们会碰到下面这些问题: OutOfMemoryError, ...

  5. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解

    参考来自:周志明著 深入理解JAVA虚拟机 https://www.cnblogs.com/therunningfish/p/5524238.html http://www.tuicool.com/a ...

  6. JVM 性能调优监控工具之Jmap和 Jhat

    JVM 性能调优监控工具之Jmap和 Jhat 文章目录 JVM 性能调优监控工具之Jmap和 Jhat 前言 一.什么是jmap和jhat? 二.方式一 1.jmap导出堆内存 2.以本人项目服务器 ...

  7. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解 | 必须收藏!

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 作者 | 优雅先生 来源 | my.oschina.net/feichexia/blog/1 ...

  8. JVM 性能调优监控工具 jps、jstack、jmap、jhat、jstat、hprof 使用详解

    A.jps(Java Virtual Machine Process Status Tool) B.jstack C.jmap(Memory Map)和jhat(Java Heap Analysis ...

  9. python 性能优化监控工具_推荐一款非常实用的JVM性能调优监控工具(亲测好用)...

    前言 现实企业级Java开发中,有时候我们会碰到下面这些问题: 1.OutOfMemoryError,内存不足 2.内存泄露 3.线程死锁 4.锁争用(Lock Contention) 5.Java进 ...

最新文章

  1. golang的临时对象池sync.Pool
  2. 5分钟教你Windows 10中将“运行”固定到开始菜单
  3. Java取当前时间,深夜思考
  4. TF之DD:利用Inception模型+GD算法生成带背景的大尺寸、高质量的Deep Dream图片——五个架构设计思维导图
  5. python获得用户输入的一个字符串(长度3)_python3 字符串属性(一)
  6. DCMTK:将XML文档的内容转换为DICOM结构的报告文件
  7. Shell脚本语言与编译型语言的差异
  8. DataTabel中关于ImpotRow的一点尝试
  9. python 列表 mysql in_关于mysql:内嵌要在python MySQLDB IN子句中使用的列表
  10. 图像处理技术(二)滤波去噪
  11. TiDB 源码阅读系列文章(二十)Table Partition
  12. 基于STM32数码相册
  13. imageview显示圆形图片
  14. ORA-27301: OS failure message: No buffer space available
  15. 云服务器能共享文件夹,云服务器能共享文件夹
  16. [LeetCode]Buy and Sell Stocks 买卖股票问题
  17. python bokeh教程_手把手教你用Bokeh进行可视化数据分析(附源码)
  18. HTTP代理中的api 代理的使用方法有什么?
  19. 基于微信电脑版HOOK开发的一套成品工具(可下载)
  20. Centos7中文字体库安装

热门文章

  1. 基于java封装的语言_封装在java中的应用
  2. crlf注入漏洞 java解决办法_HTTP响应拆分漏洞(CRLF注入攻击)解决办法
  3. mysql类 php100_PHP100中,制作自己的PHP.MYSQL类中:Access denied for user ''@'localhost' (using password: NO)...
  4. go int32不能打印0_Go并发实战--sync WaitGroup
  5. vue怎么引入js文件_Vue核心知识:8.3 vuex在vue-cli中的应用,文件之间的导出与引入...
  6. linux下批量修改文件名的方法
  7. java int.parse_java数据类型转换,parseXXX(String)或valueOf(String)有什么区别?
  8. thinkphp长连接MySQL_ThinkPHP3.2.3---mysql+ajax实现长连接,实时监测数据库数据是否更新...
  9. 计算机算法设计与分析之----- 递归与分治策略
  10. 项目质量管理:质量与质量管理概念