一、工具概述
使用命令行工具存在以下的局限性:
- 无法获取方法级别的分析数据,如方法之间的调用关系、各方法的调用次数和调用时间等
- 要去用户登陆到java应用所在的宿主机上
- 分析数据通过终端输出,结构不够直观
随着java应用的官方使用,越来越多的图形化工具问世
1、JDK自带工具
- jConsole:查看应用运行概况、监控堆信息、永久区使用情况、类加载情况等
- Visual VM:提供了可视化界面,用户查看JVM运行居于java技术应用程序的详细信息
- JMC:Java Mission Contorl 内置Java Flight Recorder,能够以极低的性能开销收集JVM的性能数据
2、第三方工具
- MAT:一个快速、功能丰富的Java heap分析工具,能够帮助使用者查找内存泄漏和减少内存消耗
- JProfiler:商业软件,功能强大
- Arthas:阿里提供的开源Java诊断工具
- Btrace:java运行时追踪工具,可以在不停机的情况下,跟踪指定的方法调用、构造函数调用和系统内存等信息
二、jConsole
1、基本概述
- 从jd1.5开始,在JDK自带的java监控和管理控制台
- 用于对JVM中内存、线程和类等的监控,是一个机遇JMX的GUI性能监控工具
2、启动
直接在cmd中输入jconsole,或者在bin下执行exe文件。
3、三种链接方式
- 本地:运行程序和启动用户需要是一个
- 远程
- Advanced
4、主要作用
三、Visual VM
1、基本概述
- 功能强大、多合一故障诊断和性能监控的可视化功能
- 显示进程、进程配置和环境信息,监控程序的CPU、GC、堆、方法区及线程信息等
- JDK6 开始之后,Visual VM随着JDK一起发布
2、插件的安装
Visual VM 自己可以安装插件 Update Center documentationhttps://visualvm.github.io/uc/release211/updates.html
3、连接方式
- 本地连接
- 远程连接
- 确定远程服务器的IP
- 田佳JMX
- 修改tomcat catalina.sh
- 在../conf中添加jmxremote.access和jmxremite.password文件
- 将服务器地址修改为公网IP地址
- 设置防火墙
- 启动tomcat,查看tomcat启动日志和端口日期
- JMX中输入端口号、用户名、密码登陆
4、主要功能
- 生成和读取dump文件,还可以针对两个不同的dump文件进行对比
- 查看JVM参数和系统属性
- 查看运行中的JVM进程
- 生成和读取线程快照
- 程序资源的实时监控
- JXM代理连接、远程环境监控、CPU分析和内存分析
四、eclipse MAT(分析Dump文件)
1、基本概述
MAT是一款功能强大的JAVA堆内存分析工具。用于查找内存泄漏以及查看内存消耗情况。如果需要分析dump文件,那么推荐使用MAT,因为MAT主要功能就是分析dump文件。
2、获取堆dump文件
- dump文件内容
- 所有对象信息,包括对象实例、成员变量、存储于栈中的基本类型和存储于堆中的其它对象引用值
- 所有类信息,包括ClassLoader、类名称、父类、静态变量等
- GCRoot到所有这些对象的引用路径
- 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)
- 两点说明
- MAT是一种工具,但是格式也是有要求的,主流的JVM生产的dump都能识别
- 最吸引人的地方,就是生成内存泄漏报表
- 获取dump文件
- 使用jmap生成 dump文件
- 配置jvm参数,当出现OOM的时候,生成dump文件,获取FUllGC之前生成
- 使用VisualVM,也是可以生成
- MAT也可以生成dump文件进行导出,生产情况下常见组合就是MAT+jmap
3、分析堆dump文件
打开一个dump文件:
打开完成后:
文件夹会生成相关文件:
- histogram:
- 可以查看某个类的强引用、GCRoot、ClassLoader
- 对比2个dump文件
- thread overview
- 查看某个线程执行情况
- <local> 就是线程的局部变量,同时可以局部变量的内存接口、存储的类型
- 线程局部变量引用信息,查看一个对象,可能存在某个静态变量或者生命周期比较长的对象对其进行引用了,导致对象无法回收,这样我们可以修改成为弱引用
- 获得对象相互引用的关系
- 浅堆与深堆
- shallow heap:浅堆,所消耗的内存,不包含引用的对象大小,只包含引用的大小
- retained heap:深堆,对象保留集中所有对象的浅堆之和,也就是对象回收后可以释放的真实空间
- 对象的保留集:只能通过A对象直接或间接访问到的所有对象的集合,仅被A持有的对象的集合
- 补充:对象实际大小,对象可以触及的所有对象的浅堆之和,和垃圾回收没关系
- A浅堆:A自己
- A深堆:A+D
- A对象:A+C+D
- 支配树:体现对象实例之间的支配关系。所有指向B的路径都经过了对象A,则称为A支配B,如果对象A是离对象B最近的支配者,那么称为A是B的直接支配者,MAT中的支配树,就是显示了一个对象的深堆
五、内存泄漏
1、内存泄漏的理解与分类
对象不会被使用,但是GC无法回收,或者对象的生命中后期跟JVM一样,都可以称为内存泄漏。一个对象在被使用,但是并不被需要。内存泄漏时间长了就会出现内存溢出。
2、java中内存泄漏的8中情况
- 静态集合类:定义静态集合,一直添加对象,导致对象无法回收
- 单例模式:和静态集合类类似,因为单例的静态特征,它的生命周期和JVM相同,所以单例对象如果持有外部对象的引用,那么这些对象也不会被释放
- 内部类持有外部类:一个外部类的实例返回了一个内部的实例,这个内部类被长期饮用了,即使外部类实例不在被使用,但由于内部类持有外部类的实例,这个外部类不会被垃圾回收
- 各种连接:数据流连接、数据流需要显示的关闭,就会内存泄漏
- 变量不合理的作用域
- 改变哈希值:当一个对象放入HashSet中后,就不能改变这个对象那些参与计算哈希值的字段了,否则由于修改后哈希值与当初存入HashSet集合中的哈希值就不同了,在这种情况下,即使在contains方法使用该对象当前的引用作为参数去HashSet检索对象,也讲返回找不到对象的结果,也会导致无法从HashSet单独删除当前对象
package memory.leak;
import java.util.HashSet;
public class ChangeHashCode {
public static void main(String[] args) {
HashSet<Point> hs = new HashSet<>();
Point cc = new Point();
cc.setX(10);
hs.add(cc);
cc.setX(20);
System.out.println("hs.remove = " + hs.remove(cc));//hs.remove = false
hs.add(cc);
System.out.println("hs.size = " + hs.size());//hs.remove = false
}
}
class Point{
int x;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
@Override
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + x;
return result;
}
@Override
public 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;
}
}
- 缓存泄漏:内存中的HashMap需要及时清理,可以改用WeakHashMap
- 监听器和回调
3、内存泄漏案例分析
package memory.leak.c2;
import java.util.Arrays;
import java.util.EmptyStackException;
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
ensureCapacity();
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
elements[size ++] = e;
}
public Object popLeak(){
if(size == 0){
throw new EmptyStackException();
}
//只是把置针向下移动了,数组内的对象并没有回收。就是数组内的对象并没有回收
return elements[--size];
}
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);
}
}
}
六、支持OQL语言查询对象信息
- SELECT:select * from "包名"
- FROM
- WHERE
- 内置对象于方法
七、JProfiler
1、基本概念
1、特点
- 使用方便、界面操作友好(简单且强大)
- 对被分析的应用影响很小
- CPU、Thread、Memory分析功能尤其强大
- 支持对jdbc、noSql、jsp、servlet、socket等进行分析
- 支持多种模式(离线、在线)
- 支持本地、远程JVM监控
- 跨平台、拥有多中操作系统的安装版本
2、主要功能
- 方法调用:对方法调用的分析可以帮助使用者了解程序在做什么
- 内存分配:通过分析堆上对象、引用链和垃圾手机能助于修复内存泄漏、优化内存使用
- 线程和锁:提供多种针对线程和锁的分析试图
- 高级子系统:许多性能问题都发生在更高的语义级别上,例如JDBC的调用
2、安装与配置
在idea直接下载插件即可
3、具体使用
1、数据采集方式
- 重构模式(Instrumentation):功能强大、信息准确,但是消耗性能、影响应用,可以过滤不分析的类
- 抽样模式(Sampling):不能提供全部的功能,但是CPU消耗小、对应用影响小
正在运行的应用,建议使用抽样的方式,这样对应用影响比小。
2、遥感监测(Telemetries)
对整个监控应用的一个概览,上面能够看到内存、类、GC等相关内容
3、内存试图(Live Memory)
- 所有对象:包含名称、大小、个数,可以点击“标记当前”,跟点击时间点进行对比
- 分配调用树:显示一个请求树或者方法、类、包或对已选择类有逮住是的饭呢佩信息的J2EE组件
- 分配热点:显示列表,包括类、方法、包或分配已选择的J2EE组件
- 类追踪器:类跟踪试图可以包含任意的数量的图表,显示选定的类和包的实例与时间
分析:内存中对象情况
- 频繁创建的对象:就是图中很多的对象,死循环、循环次数过多
- 存在大对象:查看对象的大小
- 存在内存泄漏的问题:每次垃圾就回收之后,都有内存没有被回收,并且逐步增加,这个时候可以开启记录对象
4、堆遍历(heap walker)
5、CPU试图(CUP Views)
主要是针对应用中方法的调用时间判断,调用时间越长占用CPU时间越长
调用百分比、执行时间、调用次数、方法所在类的完整路径
6、线程试图(threads)
线程分析:
- web容器线程最大数,例如tomcat线程容量应该大于并发数
- 线程阻塞
- 线程死锁
- 线程历史:显示一个与线程活动和线程状态一起的活动时间表
- 线程监控:显示一个列表,包括所有活动的线程以及它们目前的活动情况
- 线程转出:显示有所线程的堆栈信息(Thread dump)
7、监视器和锁(Monitor&lock)
八、Arthas(线上排查问题)
1、基本概述
1、背景
真正的项目都是在服务器上的,所以如果使用图形化界面,那么一定需要参数配置和网络连接,不仅不方便并且消耗性能。居于这个场景,Arthas诞生了。
2、概述
Arthas是一款开源、流行的命令行诊断工具,在线上排查问题,无需重启,动态跟踪代码,实时监控JVM状态。
能够解决的问题:
- 这个类从哪个jar包加载的?为什么会报各种类相关的Exception
- 改的代码为什么没有被执行到?没有commit?分支搞错了?
- 遇到线程问题无法debug,难道只能通过日志重新发布吗?
- 线上遇到某个用户的数据处理有问题,但是线上无法debug,线下无法重现
- 是否有一个全局视角查看系统运行的状况
- 有什么办法可以监控到JVM实时运行状态
3、基于哪些工具开发而来
4、官方使用文档
2、安装和使用
解压完毕之后:
直接启动 arthas-boot.jar即可,启动后选择一个进程去监控,也可以直接跟着PID监控指定进程:
http://127.0.0.1:8563/,使用web控制台进行查看。
查看日志:cat ~/logs/arthas/arthas.log,也可以使用 java -jar arthas-boot.jar -h 查看帮助文档。
3、相关诊断命令
1、基础指令
- help:帮助
- cat:打印文件内容
- echo:打印相关参数
- grep:匹配查找
- tee:复制标准输入到标准输出和指定文件
- pwd:返回当前工作目录
- cls:清屏
- session:查看当前回话信息
- reset:重置增强类,arthas增强过类全部还原,关闭时候也会重置
- version:输出当前目标进程所加载的arthas的版本号
- history:打印历史命令
- quite:退出当前客户端
- stop:关闭arthas服务器,所有客户端都退出
- keymap:自定义快捷键
2、JVM相关
- dashboard:查看程序控制面板 -i 时间间隔、-n 打印次数
- thread,查看线程情况,可以根据线程ID查询详情,-b 参数是查看是否有死锁,-i 统计时间段内线程cpu使用率, -n 查询cpu使用率最高的几个线程
- jvm:查看jvm相关信息
- heapdump:保存线程dump文件,可以使用--live只查看或者的对象
3、class/classLoader相关
- mc、redefine:mc编译文件,redefine进行替换
- sc:JVM已经加载类的信息,-d -f 类的详细信息
- sm:JVM已经加载类方法的信息
- jad:查看类的源码,也可以针对某个方法
- classloader 查看类的加载器
4、monitor、watch、trace相关
- stack:输出方法当前被调用的调用路径
- monitor:方法执行监控
- trace:方法内部调用路径,并输出方法路径上的耗时
- watch:方法执行数据观测
- tt:方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下进行观测
5、其它
- profiler/火焰图
- options
九、Java Mission Control
JDK自带的工具,取样的方式比较优秀,对程序营销非常非常的小
十、其它工具
- 火焰图:展示CPU时间分配的工具,改善接口性能,x轴是CUP时间,y轴是栈调用情况
- Tprofiler:阿里提供的性能,能够定位性能瓶颈代码
- Btrace:动态追踪java应用
- YourKit、JProbe、Spring Insight等工具