1.工具概述

1.1 图形化总和诊断工具:

  1. JDK自带的工具

  2. 第三方工具

2. jConsole(了解)

  • 从java5开始,在JDK中自带的java监控和管理控制台。
  • 用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。

2.1 启动

  • jdk/bin目录下,启动jconsole.exe命令即可
  • 不需要使用jps命令来查询

2.2 三种连接方式

  • Local:
    console连接一个正在本地系统运行的JVM,并且执行程序的和运行Jconsole的需要是同一个用户。Jconsole使用文件系统的授权通过RMI连接器连接到平台的MBean服务器上。这种从本地连接的监控能力只有Sun的JDK具有

  • Remote:
    使用下面的URL通过RMI连接器连接到一个JMX代理,service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi。JConsole为建立连接,需要在环境变量中设置mx.remote.credentials来指定用户名和密码,从而进行授权。

  • Advanced:
    使用一个特殊的URL连接JMX代理。一般情况使用自己定制的连接器而不是RMI提供的连接器来连接JMX代理,或者是一个使用JDK1.4的实现了JMX和JMX Rmote的应用

2.3 作用:

  1. JVM运行概览:
  2. 内存
  3. 线程
  4. 类:
  5. 检测死锁:

3. Visual VM

3.1 概述

  • Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具
  • 它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole
  • 在JDK6 update7以后,Visual VM便作为JDK的一部分发布(JDK/bin),即完全免费
  • 此外,Visual VM也可以作为独立的软件安装
  • 也可以在IDEA中安装,然后配置IDEA中visual vm的位置,绑定使用

3.2 插件的安装

  1. 方式一:网络下载

  2. 方式二:软件的工具栏->插件中直接下载

3.3 连接方式:

3.4 主要功能

具体的使用可以看视频、或者自己操作软件学习与使用

  1. 概述:(jinfo)

  2. 监视

  3. 线程

  4. GC

3.3.1 生成与分析dump文件:

VisualVM中监视和线程的右上角都有Dump按钮,可以对当前JVM的情况进行dump,dump下来后可以点击文件->装入,进行dump文件分析,同时还可以比较两个不同时间段的dump文件

这里不多展示,但是较为重要,记得自己点击用着看看

3.3.2 CPU与内存抽样

可以对某一时刻的CPU和内存进行快照,然后查看CPU突然飙高的原因,以及内存中类过多的问题。

4. Eclipse MAT

4.1 基本概述:

主要作用:分析dump文件


4.2 获取堆dump文件

4.2.1 dump文件内容

4.2.2 两点说明

4.2.3 获取dump文件

4.3 分析堆dump文件

4.3.1 histogram:直方图

主要列举的内存中加载的类的个数,以及占用的浅堆及深堆的大小

点击具体的类可以看到:

分组:

排查GC roots



比较基于b跟a的dump文件的区别:

4.3.2 thread overview

可用于分析有可能出现内存泄漏的线程

两个用处:

  1. 查看系统中的java线程
  2. 查看局部变量的信息

系统中的线程:

局部变量:

分析可能出现的问题:

4.3.3 获取对象相互引用的关系

incoming:被谁引用

outcoming:引用过谁

4.3.4 浅堆和深堆(shallow heap和retained heap)
  1. 浅堆:

  2. 深堆:当前对象的浅堆大小+所有只由他触及的对象的浅堆大小之和

  3. 补充:对象实际大小 (对象所能触及的浅堆大小之和)

4.3.5 案例分析

代码:

/*** 有一个学生浏览网页的记录程序,它将记录 每个学生访问过的网站地址。* 它由三个部分组成:Student、WebPage和StudentTrace三个类** 获取dump文件:-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=c:\code\student.hprof* */
public class StudentTrace {static List<WebPage> webpages = new ArrayList<WebPage>();public static void createWebPages() {for (int i = 0; i < 100; i++) {WebPage wp = new WebPage();wp.setUrl("http://www." + Integer.toString(i) + ".com");wp.setContent(Integer.toString(i));webpages.add(wp);}}public static void main(String[] args) {createWebPages();//创建了100个网页//创建3个学生对象Student st3 = new Student(3, "Tom");Student st5 = new Student(5, "Jerry");Student st7 = new Student(7, "Lily");for (int i = 0; i < webpages.size(); i++) {if (i % st3.getId() == 0)st3.visit(webpages.get(i));if (i % st5.getId() == 0)st5.visit(webpages.get(i));if (i % st7.getId() == 0)st7.visit(webpages.get(i));}webpages.clear();System.gc();}
}class Student {private int id;private String name;private List<WebPage> history = new ArrayList<>();public Student(int id, String name) {super();this.id = id;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<WebPage> getHistory() {return history;}public void setHistory(List<WebPage> history) {this.history = history;}public void visit(WebPage wp) {if (wp != null) {history.add(wp);}}
}class WebPage {private String url;private String content;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}
}

打开生成dump文件:查看线程信息:

浅堆是24是因为student包括3个字段:int4个字节、name4个字节、list是4个字节、对象头8个字节,最后补齐24个字节

具体的student内容:
15个webpage
每个对应152个字节 15*152=2280字节 --> 即为elementData的实际大小

为什么深堆回收的时候只能回收1288呢?
因为比如0、21、42、35、63、70、84既能被7整除,也能被3或者5整除,所以这7个网页在回收的时候是不能被回收的,所以只能回收8*152=1216,跟1288还是差了72;

那么这72个字节是什么?
15个elementData的元素,每个元素4个字节,即60个字节+对象头8个字节+数组本身4个字节 = 72个字节。

4.3.6 支配树



4.4 支持使用OQL语言查询对象信息

MAT支持一种类似于SQL的查询语言OQL(Object Query Language)。OQL使用类SQL语法,可以在堆中进行对象的查找和筛选。

4.4.1 select语句


4.4.2 from子句



找地址对应的结构,如果直接用student查询的话,可能会查到多个加载器加载的stduent,如果用地址的话查到的就是唯一的我们想要的student

4.4.3 where子句


5. 再谈内存泄漏

5.1 什么是内存泄漏:

5.2 内存泄漏的理解:

严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。

但是实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致OOM,也可以叫做宽泛意义上的“内存泄漏”。

5.3 内存泄漏和内存溢出的关系

5.4 java内存泄露的8种情况

5.4.1. 静态集合类

5.4.2. 单例模式

5.4.3. 内部类持有外部类

5.4.4. 各种连接,如数据库连接、网络连接和IO连接等

5.4.5. 变量不合理的作用域

5.4.6. 改变哈希值

// 例一:
/*** 演示内存泄漏* @create 14:43*/
public class ChangeHashCode {public static void main(String[] args) {HashSet set = new HashSet();Person p1 = new Person(1001, "AA");Person p2 = new Person(1002, "BB");set.add(p1);set.add(p2);p1.name = "CC";//导致了内存的泄漏set.remove(p1); //删除失败System.out.println(set);set.add(new Person(1001, "CC"));System.out.println(set);set.add(new Person(1001, "AA"));System.out.println(set);}
}class Person {int id;String name;public Person(int id, String name) {this.id = id;this.name = name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Person)) return false;Person person = (Person) o;if (id != person.id) return false;return name != null ? name.equals(person.name) : person.name == null;}@Overridepublic int hashCode() {int result = id;result = 31 * result + (name != null ? name.hashCode() : 0);return result;}@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +'}';}
}
// 例2:
/*** 演示内存泄漏* @create 14:47*/
public class ChangeHashCode1 {public static void main(String[] args) {HashSet<Point> hs = new HashSet<Point>();Point cc = new Point();cc.setX(10);//hashCode = 41hs.add(cc);cc.setX(20);//hashCode = 51  此行为导致了内存的泄漏System.out.println("hs.remove = " + hs.remove(cc));//falsehs.add(cc);System.out.println("hs.size = " + hs.size());//size = 2System.out.println(hs);}}class Point {int x;public int getX() {return x;}public void setX(int x) {this.x = x;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null) return false;if (getClass() != obj.getClass()) return false;Point other = (Point) obj;if (x != other.x) return false;return true;}@Overridepublic String toString() {return "Point{" +"x=" + x +'}';}
}
5.4.7. 缓存泄露

 例子:
/*** 演示内存泄漏** @create 14:53*/
public class MapTest {static Map wMap = new WeakHashMap();static Map map = new HashMap();public static void main(String[] args) {init();testWeakHashMap();testHashMap();}public static void init() {String ref1 = new String("obejct1");String ref2 = new String("obejct2");String ref3 = new String("obejct3");String ref4 = new String("obejct4");wMap.put(ref1, "cacheObject1");wMap.put(ref2, "cacheObject2");map.put(ref3, "cacheObject3");map.put(ref4, "cacheObject4");System.out.println("String引用ref1,ref2,ref3,ref4 消失");}public static void testWeakHashMap() {System.out.println("WeakHashMap GC之前");for (Object o : wMap.entrySet()) {System.out.println(o);}try {System.gc();TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("WeakHashMap GC之后");for (Object o : wMap.entrySet()) {System.out.println(o);}}public static void testHashMap() {System.out.println("HashMap GC之前");for (Object o : map.entrySet()) {System.out.println(o);}try {System.gc();TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("HashMap GC之后");for (Object o : map.entrySet()) {System.out.println(o);}}}
结果:
String引用ref1,ref2,ref3,ref4 消失
WeakHashMap GC之前
obejct2=cacheObject2
obejct1=cacheObject1
WeakHashMap GC之后
HashMap GC之前
obejct4=cacheObject4
obejct3=cacheObject3
Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'
HashMap GC之后
obejct4=cacheObject4
obejct3=cacheObject3
分析:

5.4.8. 监听器和回调

5.5 内存泄漏案例分析

5.5.1 案例一:
代码:
public class Stack {private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];}public void push(Object e) { //入栈ensureCapacity();elements[size++] = e;}public Object pop() { // 出栈if (size == 0)throw new EmptyStackException();Object result = elements[--size];elements[size] = null;return result;}private void ensureCapacity() {if (elements.length == size)elements = Arrays.copyOf(elements, 2 * size + 1);}
}




5.6.2 案例2:

代码:

6. Jprofiler

可查看官方文档,做的很好。地址:Jprofiler官方文档

6.1 概述

6.1.1 特点

6.1.2 主要功能
  1. 方法调用:对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法
  2. 内存分配:通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄露问题,优化内存使用
  3. 线程和锁:JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题
  4. 高级子系统:许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析

6.2 下载安装

收费、破解版自己下载,此处略。

6.2.1 Jprofiler集成IDEA
  1. 关闭IDEA

  2. session(会话) -> IDE Integration(IDE集成)

  3. 选择自己的idea版本,点击integrate

  4. 选择自己的idea的配置文件

  5. 完成

6.2.2 IDEA集成Jprofiler
  1. 直接在idea的plugins中搜索Jprofiler安装
  2. tools中设置
6.2.3 执行程序

选中需要进行监控的程序,检点小蓝点执行:

Jprofiler中就能看到监控信息:

6.3 数据采集方式


内存泄漏用Sampling方式就ok了。

6.4 具体使用

6.4.1 遥测:

6.4.2 实时内存Live Memory



注意:其中记录对象最好在内存泄漏时使用,因为资源占用较高。


注意上面的SIZE是浅堆。

  1. mark current:内存前后对比:
  2. 根据记录对象判断是否有内存泄漏的情况:比如如果在垃圾回收后,剩余的存活对象在稳步上升,说明可能存在内存泄漏的情况


6.4.3 堆遍历Heap walker

点击Picture。
查找谁使用Picture或者Picture使用了谁:

查看结果,并根据结果去看对应的图表:

以下是图表的展示情况:

6.4.4 cpu视图 cpu views


访问树:方法访问时间越长,说明cpu占用越多。(其中百分比表示下面被上面被调用的可能性,ms表示时间,inv表示调用次数)。

方法统计:

6.4.5 线程视图 Threads

程序线程监控:

创建线程dump:

6.4.6 监视器和锁 Monitors&locks

6.5 案例分析:

6.5.1 案例1:
/*** 功能演示测试* @create 12:19*/
public class JProfilerTest {public static void main(String[] args) {while (true){ArrayList list = new ArrayList();for (int i = 0; i < 500; i++) {Data data = new Data();list.add(data);}try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}
class Data{private int size = 10;private byte[] buffer = new byte[1024 * 1024];//1mbprivate String info = "hello,atguigu";
}

6.5.2 案例2:
public class MemoryLeak {public static void main(String[] args) {while (true) {ArrayList beanList = new ArrayList();for (int i = 0; i < 500; i++) {Bean data = new Bean();data.list.add(new byte[1024 * 10]);//10kbbeanList.add(data);}try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}}class Bean {int size = 10;String info = "hello,atguigu";// 注意是类变量static ArrayList list = new ArrayList();
}

进入jprofiler,可以看到内存和类在不断变大,同时GC是正常执行的:


点击byte数组,分析heap walker:

查看都被谁引用了:

查看具体的引用关系:

可以看出byte[]来自于Bean类是的list中,并且这个list是ArrayList类型的静态集合,所以找到了:static ArrayList list = new ArrayList();发现list是静态的,这不妥,因为我们的目的是while结束之后Bean对象被回收,并且Bena对象中的所有字段都被回收,但是list是静态的,那就是类的,众所周知,类变量随类而生,随类而灭,因此每次我们往list中添加值,都是往同一个list中添加值,这会造成list不断增大,并且不能回收,所以最终会导致OOM。

7. Arthas

建议本章节全部查看官方文档,不要看我写的。。。arthas官方文档

7.1 概述

7.1.1 jvisualvm和jprofiler的优缺点:

7.1.2 概述:

7.1.3 基于那些工具组合而成:

7.2 安装和使用:

见官方文档:arthas官方文档

7.2.1 安装:


7.2.2 卸载:

7.2.3 工程目录:

7.2.4 启动

7.2.5 一些操作:
# 查看进程:
jps
#查看日志
cat ~/logs/arthas/arthas.log
#查看帮助
java -jar arthas-boot.jar -h
# 退出:
quit\exit
# 关闭:
stop\shutdown
7.2.6 web console

7.3 相关诊断指令

可以看官方文档,建议直接查看官方文档。

7.3.1 基础指令:

  1. 根据jps出的java进程,启动arthas监控某个进程:
  2. help:查看命令帮助,以及查看某个命令具体怎么使用:
7.3.2 jvm相关:

  1. dashboard:可以看到重复的数据有很多个,是因为每隔一段时间就会打印一次,可以通过dashboard -i 500设置间隔500毫秒打印一次,同时可以设置dashboard -n 4设置打印4次,可以dashboard -i 1000 -n 4一起配置

  2. thread:查看线程线程相关,可以thread id查看具体线程信息,thread -b查看死锁的情况,同样的也有thread -i 5000查看5秒内cpu利用率,thread -n 2查看cpu使用率前2的线程

7.3.3 class/classloader相关:

  1. sc:

  2. sm:

  3. jad:将class文件反编译成java文件

  4. mc和redefine配合使用:

  5. ClassLoader

7.3.4 monitor/watch/trace相关:

  1. monitor:

  2. watch

  3. trace

  4. stack:

8. Java Mission Control

jdk自带的分析工具:我看了下jdk8里面有,jdk11以后的版本没有了。

想要学习的可以自己看视频,这里不多说,视频子啊P359~P360

9. 其他工具

  1. Flame Graphs火焰图

  2. Tprofiler

  3. Btrace

  4. Yourkit

  5. Hprobe

  6. Spring Insight

JVM(二十二) -- 性能监控与调优(三) -- JVM监控及诊断工具--GUI篇相关推荐

  1. JVM 学习笔记二十六、JVM监控及诊断工具-GUI篇

    二十六.JVM监控及诊断工具-GUI篇 1.工具概述 使用上一张命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但他们存在下列局限: (1)无法获取方法级别的分析数据,如方法间的调用关系 ...

  2. 性能监控与调优篇之【3. JVM 监控及诊断工具-GUI 篇】

    文章目录 3. JVM 监控及诊断工具-GUI 篇 3.1. 工具概述 3.2. JConsole 3.3. Visual VM 3.4. Eclipse MAT 3.5. JProfiler 3.6 ...

  3. <JVM下篇:性能监控与调优篇>03-JVM监控及诊断工具-GUI篇

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  4. JVM监控及诊断工具-GUI篇

    3.JVM监控及诊断工具-GUI篇 一.工具概述 使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限: 1.无法获取方法级别的分析数据,如方法间的调用关系.各方 ...

  5. 20.JVM监控以及诊断工具-GUI篇

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 20. JVM监控及诊断工具-GUI篇 20.1. 工具概述 使用上一章命令行工具或组合能帮您获取目标Java应用性能相 ...

  6. 第24章 JVM监控及诊断工具-GUI篇

    第24章 JVM监控及诊断工具-GUI篇 来自尚硅谷宋红康老师讲解的JVM:bilibili链接 1 工具概述 使用上一张命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但他们存在下列局 ...

  7. 干货满满【JVM监控及诊断工具-GUI篇】

    [JVM监控及诊断工具-GUI篇] 3.1. 工具概述 使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限: 1.无法获取方法级别的分析数据,如方法间的调用关系 ...

  8. 尚硅谷JVM下篇:性能监控与调优篇_03_JVM监控及诊断工具-GUI篇

    目录 文章目录 目录 01-工具概述 02-JConsole 基本概述 启动 三种连接方式 Local Remote Advanced 主要作用 1.概览 2.内存 3.线程 4.概要 03-Visu ...

  9. 15、JVM监控及诊断工具-GUI篇

    文章目录 第1章.工具概述 第2章.jConsole 1.基本概述 2.启动 3.三种连接方式 [1]Local [2]Remote [3]Advanced 4.主要作用 第3章.Visual VM ...

最新文章

  1. 无线传感器网络协议与体系结构 思维导图 (book) Protocols and Architectures for Wireless Sensor Networks, Holger Karl
  2. SQL Server 找回没有备份的数据
  3. c++经典书籍--深度探索C++对象模型
  4. 如何知道远程电脑某一端口是否打开?
  5. 2016光伏创新如何突围融资困境?
  6. jekins构建触发器详解
  7. 妙到巅峰的8个简洁数学证明(文科生都能看懂),隐隐触摸到一丝只属于神的智慧气息……...
  8. VB快速查找大型文件中包含的字符串
  9. 危机十足站长的生命觉悟:拼命也得每天挤一滴墨水!
  10. 开源GIS(十九)——WKT、WKB与GeoJSON
  11. android屏蔽电话号码,手机屏蔽骚扰电话的方法【推荐方法】
  12. 推荐几款 Redis 可视化工具
  13. 【论文复现】ReLU、Leaky ReLU、PReLU、RReLU实验对比(2015)
  14. Mysql 根据经纬度计算距离
  15. python统计词频并进行可视化显示_python统计词频
  16. iOS APP审核注意事项
  17. 在微信小游戏中使用tensorflow的face-landmarks-detection
  18. android自动夜间模式,夜晚的故事(android夜间模式实现)
  19. 我是培训机构出身的程序员,不敢告诉任何人!
  20. LWN:使用Jitsi进行视频会议!

热门文章

  1. django ajax传递数组
  2. AnimationDrawable 帧动画 爆炸特效
  3. KPM算法思想及实现
  4. HX711称重模块的使用
  5. angr学习之ctf练习
  6. 在 Win10 系统下安装 JDK 及配置环境变量的方法
  7. 苹果xr截屏怎么截_原来苹果手机可实现长截屏!学到了,以后不用羡慕别人手机了...
  8. 利用茉莉机器人接口开发一个简单的安卓机器人app
  9. 登录163邮箱续费情况怎么查询?163vip邮箱怎么收费?
  10. pytorch Kfold数据集划分