jdk可视化工具系列——检视阅读

参考

java虚拟机系列

RednaxelaFX知乎问答

RednaxelaFX博客

JConsole——Java监视与管理控制台

jconsole介绍

JConsole(java monitoring and management console)是一款基于JMX(Java Management Extensions,即Java管理扩展)的可视化监视和管理工具。

启动JConsole

  1. 点击JDK/bin 目录下面的“jconsole.exe”即可启动
  2. 然后会自动搜索本机运行的所有虚拟机进程
  3. 选择其中一个进程可开始进行监控

JConsole基本介绍

JConsole 基本包括以下基本功能:概述、内存、线程、类、VM概要、MBean

运行下面的程序、然后使用JConsole进行监控;注意设置虚拟机参数

/**
* <p>
* 设置虚拟机参数:-Xms100M -Xmx100m -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/dump1.hprof
*/
public class Demo1 {
static class OOMObject {
//public byte[] placeholder = new byte[64 * 1024];
public byte[] placeholder = new byte[64 * 1024 * 1024]; //create OOM
} public static void fillHeap(int num) throws InterruptedException {
Thread.sleep(20000); //先运行程序,在执行监控
List<OOMObject> list = new ArrayList<OOMObject>();
for (int i = 0; i < num; i++) {
// 稍作延时,令监视曲线的变化更加明显
Thread.sleep(50);
list.add(new OOMObject());
}
System.gc();
} public static void main(String[] args) throws Exception {
fillHeap(1000);
while (true) {
//让其一直运行着
}
}
}

打开JConsole查看上面程序

可以切换顶部的选项卡查看各种指标信息。

内存监控——可视化的jstat 命令

“内存”页签相当于可视化的jstat 命令,用于监视受收集器管理的虚拟机内存的变换趋势。

还是上面的程序:

代码运行,控制台也会输出gc日志:(因为我们添加了参数:-XX:+PrintGCDetails)

、[GC (Allocation Failure) [DefNew: 27273K->3392K(30720K), 0.0120476 secs] 27273K->15632K(99008K), 0.0120955 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 30667K->3392K(30720K), 0.0152203 secs] 42907K->38103K(99008K), 0.0152500 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[GC (Allocation Failure) [DefNew: 30710K->3376K(30720K), 0.0297297 secs] 65422K->63609K(99008K), 0.0297929 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
[Full GC (System.gc()) [Tenured: 60232K->66947K(68288K), 0.0107243 secs] 67227K->66947K(99008K), [Metaspace: 9501K->9501K(1058816K)], 0.0107834 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 27328K->27328K(30720K), 0.0000201 secs][Tenured: 66947K->2319K(68288K), 0.0147126 secs] 94275K->2319K(99008K), [Metaspace: 9724K->9724K(1058816K)], 0.0148269 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] //placeholder加大测试生产dump文件
[GC (Allocation Failure) [DefNew: 4946K->1232K(30720K), 0.0019894 secs][Tenured: 65536K->66767K(68288K), 0.0027748 secs] 70482K->66767K(99008K), [Metaspace: 3477K->3477K(1056768K)], 0.0048224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 66767K->66746K(68288K), 0.0016820 secs] 66767K->66746K(99008K), [Metaspace: 3477K->3477K(1056768K)], 0.0017023 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:/dump1.hprof ...
Unable to create D:/dump1.hprof: File exists
Heap
def new generation total 30720K, used 820K [0x00000000f9c00000, 0x00000000fbd50000, 0x00000000fbd50000)
eden space 27328K, 3% used [0x00000000f9c00000, 0x00000000f9ccd0e0, 0x00000000fb6b0000)
from space 3392K, 0% used [0x00000000fba00000, 0x00000000fba00000, 0x00000000fbd50000)
to space 3392K, 0% used [0x00000000fb6b0000, 0x00000000fb6b0000, 0x00000000fba00000)
tenured generation total 68288K, used 66746K [0x00000000fbd50000, 0x0000000100000000, 0x0000000100000000)
the space 68288K, 97% used [0x00000000fbd50000, 0x00000000ffe7e980, 0x00000000ffe7ea00, 0x0000000100000000)
Metaspace used 3512K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.self.test.Demo1$OOMObject.<init>(Demo1.java:19)
at com.self.test.Demo1.fillHeap(Demo1.java:28)
at com.self.test.Demo1.main(Demo1.java:34)

线程监控——可视化的jstack命令

如果上面的“内存”页签相当于可视化的jstat命令的话,“线程”页签的功能相当于可视化的jstack命令,遇到线程停顿时可以使用这个页签进行监控分析。线程长时间停顿的主要原因主要有:等待外部资源(数据库连接、网络资源、设备资源等)、死循环、锁等待(活锁和死锁)

下面三个方法分别等待控制台输入、死循环演示、线程锁等待演示

第一步:运行如下代码:

public class Demo2 {
public static void main(String[] args) throws IOException {
waitRerouceConnection();
createBusyThread();
createLockThread(new Object());
} /**
* 等待控制台输入
*
* @throws IOException
*/
public static void waitRerouceConnection() throws IOException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}, "waitRerouceConnection");
thread.start(); } /**
* 线程死循环演示
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
;
}
}
}, "testBusyThread");
thread.start();
} /**
* 线程锁等待演示
*/
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}
}

第二步:打开jconsole中查看上面程序运行情况,可以查看到3个目标线程

第三步:查看目标线程信息

waitRerouceConnection线程处于读取数据状态,如上图:

testBusyThread线程位于代码45行,处于运行状态,如下图:

testLockThread处于活锁等待状态,如下图:

只要lock对象的notify()或notifyAll()方法被调用,这个线程便可能激活以继续执行

通过“线程”这个窗口可以很方便查询虚拟机中的线程堆栈信息,对发现系统中的一些问题非常有帮助。

线程死锁演示

第一步:运行下面代码:

package com.jvm.jconsole;

/**
* <b>description</b>: <br>
* <b>time</b>:2019/6/2 22:39 <br>
* <b>author</b>:ready likun_557@163.com
*/
public class Demo3 {
public static void main(String[] args) {
User u1 = new User("u1");
User u2 = new User("u2");
Thread thread1 = new Thread(new SynAddRunalbe(u1, u2, 1, 2, true));
thread1.setName("thread1");
thread1.start();
Thread thread2 = new Thread(new SynAddRunalbe(u1, u2, 2, 1, false));
thread2.setName("thread2");
thread2.start();
} /**
* 线程死锁等待演示
*/
public static class SynAddRunalbe implements Runnable {
User u1, u2;
int a, b;
boolean flag; public SynAddRunalbe(User u1, User u2, int a, int b, boolean flag) {
this.u1 = u1;
this.u2 = u2;
this.a = a;
this.b = b;
this.flag = flag;
} @Override
public void run() {
try {
if (flag) {
synchronized (u1) {
Thread.sleep(100);
synchronized (u2) {
System.out.println(a + b);
}
}
} else {
synchronized (u2) {
Thread.sleep(100);
synchronized (u1) {
System.out.println(a + b);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static class User {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public User(String name) {
this.name = name;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}

thread1持有u1的锁,thread2持有u2的锁,thread1等待获取u2的锁,thread2等待获取u1的锁,相互需要获取的锁都被对方持有者,造成了死锁。程序中出现了死锁的情况,我们是比较难以发现的。需要依靠工具解决。刚好jconsole就是这个美妙的工具。

第二步:在jconsole中打开上面程序的监控信息:

从上面可以看出代码48行和55行处导致了死锁。

关于程序死锁的,我们还可以使用命令行工具jstack来查看java线程堆栈信息,也可以发现死锁。

jvisualvm——多合一故障处理工具

VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。本文主要介绍如何使用 VisualVM 进行性能分析及调优。

VisualVM位于{JAVA_HOME}/bin目录中。

查看jvm配置信息

第一步:双击左边窗口显示正在运行的java进程

第二步:点击右侧窗口“概述”,可以查看各种配置信息

通过jdk提供的jinfo命令工具也可以查看上面的信息。

查看cpu、内存、类、线程监控信息——检视tab页

查看堆的变化

步骤一:运行下面的代码

每隔3秒,堆内存使用新增100M

public class Demo1 {
public static final int _1M = 1024 * 1024; public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new byte[100 * _1M]);
TimeUnit.SECONDS.sleep(3);
System.out.println(i);
}
}
}

步骤二:在VisualVM可以很清晰的看到堆内存变化信息。

查看堆快照

步骤一:点击“监视”->"堆(dump)"可以生产堆快照信息.

生成了以heapdump开头的一个选项卡,内容如下:

对于“堆 dump”来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。

导出堆快照文件

步骤一:查看堆快照,此步骤可以参考上面的“查看堆快照”功能

步骤二:右键点击另存为,即可导出hprof堆快照文件,可以发给其他同事分析使用

查看class对象加载信息

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

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

public class Demo2 {

    private static List<Object> insList = new ArrayList<Object>();

    public static void main(String[] args) throws Exception {
permLeak();
} private static void permLeak() throws Exception {
for (int i = 0; i < 2000; i++) {
URL[] urls = getURLS();
URLClassLoader urlClassloader = new URLClassLoader(urls, null);
Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true, urlClassloader);
Method getLog = logfClass.getMethod("getLog", String.class);
Object result = getLog.invoke(logfClass, "TestPermGen");
insList.add(result);
System.out.println(i + ": " + result);
if (i % 100 == 0) {
TimeUnit.SECONDS.sleep(1);
}
}
} private static URL[] getURLS() throws MalformedURLException {
File libDir = new File("D:\\installsoft\\maven\\.m2\\repository3.3.9_0\\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;
} }

步骤二:打开visualvm查看,metaspace

CPU分析:发现cpu使用率最高的方法

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

下面我们写一个cpu占用率比较高的程序。

步骤一:运行下列程序:

public class Demo3 {
public static void main(String[] args) throws InterruptedException {
cpuFix();
} /**
* cpu 运行固定百分比
*
* @throws InterruptedException
*/
public 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);
}
}
}

步骤二:打开visualvm查看cpu使用情况,我的电脑是4核的,如下图:

过高的 CPU 使用率可能是我们的程序代码性能有问题导致的。可以切换到“抽样器”对cpu进行采样,可以查看到哪个方法占用的cpu最高,然后进行优化。

从图中可以看出cpuFix方法使用cpu最多,然后就可以进行响应的优化了。

查看线程快照:发现死锁问题

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

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

可以查看线程快照,发现系统的死锁问题。

下面我们将通过visualvm来排查一个死锁问题。

步骤一:运行下面的代码:

public class Demo4 {

    public static void main(String[] args) {
Obj1 obj1 = new Obj1();
Obj2 obj2 = new Obj2();
Thread thread1 = new Thread(new SynAddRunalbe(obj1, obj2, 1, 2, true));
thread1.setName("thread1");
thread1.start();
Thread thread2 = new Thread(new SynAddRunalbe(obj1, obj2, 2, 1, false));
thread2.setName("thread2");
thread2.start();
} /**
* 线程死锁等待演示
*/
public static class SynAddRunalbe implements Runnable {
Obj1 obj1;
Obj2 obj2;
int a, b;
boolean flag; public SynAddRunalbe(Obj1 obj1, Obj2 obj2, int a, int b, boolean flag) {
this.obj1 = obj1;
this.obj2 = obj2;
this.a = a;
this.b = b;
this.flag = flag;
} @Override
public void run() {
try {
if (flag) {
synchronized (obj1) {
Thread.sleep(100);
synchronized (obj2) {
System.out.println(a + b);
}
}
} else {
synchronized (obj2) {
Thread.sleep(100);
synchronized (obj1) {
System.out.println(a + b);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static class Obj1 {
} public static class Obj2 {
}
}

程序中:thread1持有obj1的锁,thread2持有obj2的锁,thread1等待获取obj2的锁,thread2等待获取obj1的锁,相互需要获取的锁都被对方持有者,造成了死锁。程序中出现了死锁的情况,我们是比较难以发现的。需要依靠工具解决。

步骤二:打开visualvm查看堆栈信息:

点击dump,生成线程堆栈信息,还可以右击threaddump生产线程dump分析。

可以看到“Found one Java-level deadlock”,包含了导致死锁的代码。

"thread2":
at com.jvm.visualvm.Demo4$SynAddRunalbe.run(Demo4.java:50)
- waiting to lock <0x00000007173d40f0> (a com.jvm.visualvm.Demo4$Obj1)
- locked <0x00000007173d6310> (a com.jvm.visualvm.Demo4$Obj2)
at java.lang.Thread.run(Thread.java:745)
"thread1":
at com.jvm.visualvm.Demo4$SynAddRunalbe.run(Demo4.java:43)
- waiting to lock <0x00000007173d6310> (a com.jvm.visualvm.Demo4$Obj2)
- locked <0x00000007173d40f0> (a com.jvm.visualvm.Demo4$Obj1)
at java.lang.Thread.run(Thread.java:745)

上面这段信息可以看出,thread1持有Obj1对象的锁,等待获取Obj2的锁,thread2持有Obj2的锁,等待获取Obj1的锁,导致了死锁。

总结

本文介绍了jdk提供的一款非常强大的分析问题的一个工具VisualVM,通过他,我们可以做一下事情:

  1. 查看应用jvm配置信息
  2. 查看cpu、内存、类、线程监控信息
  3. 查看堆的变化
  4. 查看堆快照
  5. 导出堆快照文件
  6. 查看class对象加载信息
  7. CPU分析:发现cpu使用率最高的方法
  8. 分析死锁问题,找到死锁的代码

jdk可视化工具系列——检视阅读的更多相关文章

  1. Java高并发系列——检视阅读

    Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...

  2. Java虚拟机系列——检视阅读

    Java虚拟机系列--检视阅读 参考 java虚拟机系列 入门掌握JVM所有知识点 2020重新出发,JAVA高级,JVM JVM基础系列 从 0 开始带你成为JVM实战高手 Java虚拟机-垃圾收集 ...

  3. JDK的可视化工具系列 (四) JConsole、VisualVM

    JConsole: Java监视与管理控制台 代码清单1: import java.util.*; public class JConsoleDemo { static class OOMObject ...

  4. JDK常用工具

    JDK的命令行工具 jps 查看正在使用的jvm机器进程号. 常用命令,-l显示正在运行的jar包或者软件(基于jvm),-v显示当前进程详细的jvm参数 jps -l jps -v javap 反汇 ...

  5. MYSQL系列1_MySQL的安装,可视化工具的使用,以及建库建表等

    大家都知道MYSQL是开源的数据库,现在MYSQL在企业中的使用也越来越多,本人之前用过SQL SERVER数据库,因业务需要和自己的兴趣想要学习MYSQL,对于MYSQL,本人还是新手,请大家多多指 ...

  6. JDK的命令行工具系列 (三) jhat、jstack

    jhat: heapdump文件分析工具 在前两篇系列文章JDK的命令行工具系列 (一) jps.jstat.JDK的命令行工具系列 (二) javap.jinfo.jmap中, 我们已经介绍过了jp ...

  7. JDK的命令行工具系列 (二) javap、jinfo、jmap

    javap: 反编译工具, 可用来查看java编译器生成的字节码 参数摘要: -help 帮助 -l 输出行和变量的表 -public 只输出public方法和域 -protected 只输出publ ...

  8. 99%的人都搞错了的java方法区存储内容,通过可视化工具HSDB和代码示例一次就弄明白了

    https://zhuanlan.zhihu.com/p/269134063  番茄番茄我是西瓜 那是我日夜思念深深爱着的人啊~ 已关注   6 人赞同了该文章 前言 本篇是java内存区域管理系列教 ...

  9. 漫谈可视化Prefuse(五)---一款属于我自己的可视化工具

    伴随着前期的基础积累,翻过API,读过一些Demo,总觉得自己已经摸透了Prefuse,小打小闹似乎已经无法满足内心膨胀的自己.还记得儿时看的<武状元苏乞儿>中降龙十八掌最后一张居然是空白 ...

随机推荐

  1. 学习go的一些笔记

    反射 reflect type CfgSt struct{ Match *int64 `ini:"match"` Desc string `ini:"desc" ...

  2. 典藏版Web功能测试用例库

    界面显示 ​ 初始界面元素:title.内容,默认值.必填项(红*) ​ 样式美观 ​ 排版规范 ​ 字体统一 ​ 编辑页面有光标,定位在第一个可编辑文本框 ​ 内容过多时,滚动条 ​ loading ...

  3. docker部署安装zabbix

    安装docker 安装docker需要配置网络yum源,centos7自带的版本太低了 第一步:下载阿里云的docker安装包 在yum仓库的目录之下 [root@localhost yum.repo ...

  4. python文档翻译之概述

    Python是一个使用方便且功能强大的变成语言.它具有高效的高级数据结构和面向对象编程特性.其优雅的语法.动态类型.解释型特性使得它成为大多数平台上脚本和快速应用程序开发的理想语言. Python解释 ...

  5. Fowsniff靶机

    Fowsniff靶机 主机探测+端口扫描. 扫目录没扫到什么,看一下页面源代码. 网站主页告诉我们这个站现在不提供服务了,并且因为收到了安全威胁,攻击者将他们管理员信息发布到了社交媒体上. 大家要科学 ...

  6. 如何下载jmeter旧版本

    推荐先用旧版本做好测试基本操作,因为高版本不适合做压力测试,需要证书,有点麻烦. 1.百度或直接打开jmeter官网:https://jmeter.apache.org/ 2.向下拖到Archives ...

  7. Android作业 0923

    计算器小应用 package com.example.myhomework2; import androidx.appcompat.app.AppCompatActivity; import andr ...

  8. MySQL中concat()、concat_ws()、group_concat()函数的使用技巧与心得总结

    Author:极客小俊 一个专注于web技术的80后 我不用拼过聪明人,我只需要拼过那些懒人 我就一定会超越大部分人! CSDN@极客小俊,原创文章, B站技术分享 B站视频 : Bilibili.c ...

  9. GZip 压缩解压 工具类 [ GZipUtil ]

    片段 1 片段 2 pom.xml <dependency> <groupId>commons-codec</groupId> <artifactId> ...

  10. Nginx+Gunicorn+Supervisor部署Flask应用

    Flask 内置了简单的 Web 环境,让我们在开发的时候只需要专注于应用实现,而真正要在生产环境运行时这个简单的 Web 环境就不够用了,还需要一系列操作才能让 Web 应用高效的运行起来.现在记录 ...