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. 一道JavaScript的二维数组求平均数的题

    JavaScript中只支持一维数组,但是可以在数组中嵌套数组来创建二维以至于多维的数组.今天下午在看书时候,发现一道感觉比较有意思的题,就是js中如何求二维数组的列之和和行之和,现在就给大家分享下, ...

  2. C#开发PACS医学影像处理系统(十七):2D处理之影像旋转和翻转

    1.任意角度旋转 在XAML设计器中,设置RotateTransform属性 <InkCanvas x:Name="ToolInkCanvas" UseCustomCurso ...

  3. Spark Driver Program剖析

    SparkContext是通往Spark集群的唯一入口,是整个Application运行调度的核心. 一.Spark Driver Program Spark Driver Program(以下简称D ...

  4. Mac使用Charles给iPhone抓包流程

    目录 需求 步骤 1. Mac打开共享Wifi 2. iPhone连接刚刚的WIFI 3. 找到Mac的局域网ip 4. 配置代理 需求 有时候手机接口出问题了,不知道从哪里下手,Charles就是非 ...

  5. Object类:又回到最初的起点

    Object类大概是每个JAVA程序员认识的第一个类,因为它是所有其他类的祖先类.在JAVA单根继承的体系下,这个类中的每个方法都显得尤为重要,因为每个类都能够调用或者重写这些方法.当你JAVA学到一 ...

  6. python爬取花木兰豆瓣影评,并进行词云分析

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取 python免费学习资 ...

  7. 微信小程序问题汇总

    一.消息推送配置 1.解析失败.请检查信息是否填写正确 服务器地址中不能使用其他的端口号,把端口号去掉,默认就是走80或443端口,另外这个地址需要外网访问,我使用了nat123映射了80端口,这个工 ...

  8. vue项目工程中npm run dev 到底做了什么

    1. npm install 安装了webpack框架中package.json中所需要的依赖 2.安装完成之后,需要启动整个项目运行,npm run 其实执行了package.json中的scrip ...

  9. 从面向过程到面向对象再到MVC

    /* * * title: 从面向过程到面向对象再到MVC * author: tanghao * date: 2020.9.30 * version: 1.0 * */ 前言 本文档通过一个显示20 ...

  10. spark-1-架构设计&基本流程

    Spark运行架构包括: (1)集群资源管理器(Cluster Manager) (2)运行作业任务的工作节点(Worker Node) (3)每个应用的任务控制节点(Driver)和每个工作节点上负 ...