JFR 与 JProfilter Jvmisualvm
只有JFR 是可以在生产环境使用 采用C++独立写的采样手机功能
而 JProfilter/JVisualvm 都只能在测试环境下使用 使用instrument 机制 ,还有debug 框架
最早是JRockit 虚拟机特有的,被Oracle 收购后合并到JDK里面
但是JFR 基于采样,准确度不及JProfilter
Java性能调优作为大型分布式系统提供高性能服务的必修课,其重要性不言而喻。
好的分析工具能起到事半功倍的效果,利用分析利器JMC、JFR,可以实现性能问题的准确定位。
本文主要阐述如何利用JFR生成性能日志
JMC:Java Mission Control
JFR:Java Flight Recorder
1. 打开JFR
JVM_OPT中添加:-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
2. 启动JFR
登陆服务器,找到应用PID,执行命令:jcmd $PID JFR.start name=abc,duration=120s
3. Dump JFR
等待至少duration(本文设定120s)后,执行命令:jcmd $PID JFR.dump name=abc,duration=120s filename=abc.jfr(注意,文件名必须为.jfr后缀)
4. 检查JFR状态
执行命令:jcmd $PID JFR.check name=abc,duration=120s
5. 停止JFR
执行命令:jcmd $PID JFR.stop name=abc,duration=120s
6. JMC分析
切回开发机器,下载步骤3中生成的abc.jfr,打开jmc,导入abc.jfr即可进行可视化分析
前段时间在给公司项目做性能分析,从简单的分析Log(GC log, postgrep log, hibernate statitistic),到通过AOP搜集软件运行数据,再到PET测试,感觉时间花了不少,性能也有一定的提升,但总感觉像是工作在原始时代,无法简单顺畅,又无比清晰的获得想要的结果。遂花费了一定的时间,从新梳理学习了一下之前用过的关于jvm调优和内存分析的各种工具,包括JDK自带的jps, jstack, jmap, jconsole,以及IBM的HeapAnalyzer等,这些工具虽然提供了不少功能,但其可用度,便捷度,远没达到IntelliJ之于Java开发那种地步。在偶然情况下,在云栖社区上发现有人推荐Jprofiler,装上使用版一用,发现果然是神器,特此推荐给大家。先声明,这个软件是商用的,网上有很多关于lisence的帖子,我这里转发,但是绝不推荐大家用破解版!
L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257
L-Larry_Lau@163.com#5481-ucjn4a16rvd98#6038
L-Larry_Lau@163.com#99016-hli5ay1ylizjj#27215
L-Larry_Lau@163.com#40775-3wle0g1uin5c1#0674
L-Larry_Lau@163.com#7009-14frku31ynzpfr#20176
L-Larry_Lau@163.com#49604-1jfe58we9gyb6#5814
L-Larry_Lau@163.com#25531-1qcev4yintqkj#23927
L-Larry_Lau@163.com#96496-1qsu1lb1jz7g8w#23479
L-Larry_Lau@163.com#20948-11amlvg181cw0p#171159
然后,先转一篇云栖上的文章,然后再慢慢开始我们的Jprofiler之旅。
一.JProfiler是什么
JProfiler是由ej-technologies GmbH公司开发的一款性能瓶颈分析工具(该公司还开发部署工具)。
其特点:
- 使用方便
- 界面操作友好
- 对被分析的应用影响小
- CPU,Thread,Memory分析功能尤其强大
- 支持对jdbc,noSql, jsp, servlet, socket等进行分析
- 支持多种模式(离线,在线)的分析
- 跨平台
(图1)
二.数据采集
Q1. JProfiler既然是一款性能瓶颈分析工具,这些分析的相关数据来自于哪里?
Q2. JProfiler是怎么将这些数据收集并展现的?
(图2)
A1. 分析的数据主要来自于下面俩部分
1. 一部分来自于jvm的分析接口**JVMTI**(JVM Tool Interface) , JDK必须>=1.6
JVMTI is an event-based system. The profiling agent library can register handler functions for different events. It can then enable or disable selected events
例如: 对象的生命周期,thread的生命周期等信息
2. 一部分来自于instruments classes(可理解为class的重写,增加JProfiler相关统计功能)
例如:方法执行时间,次数,方法栈等信息
A2. 数据收集的原理如图2
1. 用户在JProfiler GUI中下达监控的指令(一般就是点击某个按钮)
2. JProfiler GUI JVM 通过socket(默认端口8849),发送指令给被分析的jvm中的JProfile Agent。
3. JProfiler Agent(如果不清楚Agent请看文章第三部分"启动模式") 收到指令后,将该指令转换成相关需要监听的事件或者指令,来注册到JVMTI上或者直接让JVMTI去执行某功能(例如dump jvm内存)
4. JVMTI 根据注册的事件,来收集当前jvm的相关信息。 例如: 线程的生命周期; jvm的生命周期;classes的生命周期;对象实例的生命周期;堆内存的实时信息等等
5. JProfiler Agent将采集好的信息保存到**内存**中,按照一定规则统计好(如果发送所有数据JProfiler GUI,会对被分析的应用网络产生比较大的影响)
6. 返回给JProfiler GUI Socket.
7. JProfiler GUI Socket 将收到的信息返回 JProfiler GUI Render
8. JProfiler GUI Render 渲染成最终的展示效果
三. 数据采集方式和启动模式
A1. JProfier采集方式分为两种:Sampling(样本采集)和Instrumentation
Sampling: 类似于样本统计, 每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。优点是对应用影响小(即使你不配置任何Filter, Filter可参考文章第四部分),缺点是一些数据/特性不能提供(例如:方法的调用次数)
Instrumentation: 在class加载之前,JProfier把相关功能代码写入到需要分析的class中,对正在运行的jvm有一定影响。优点: 功能强大,但如果需要分析的class多,那么对应用影响较大,一般配合Filter一起使用。所以一般JRE class和framework的class是在Filter中通常会过滤掉。
注: JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型 。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据, 所以可以直接认为是JProfiler的数据采集类型。
A2: 启动模式:
Attach mode
可直接将本机正在运行的jvm加载JProfiler Agent. 优点是很方便,缺点是一些特性不能支持。如果选择Instrumentation数据采集方式,那么需要花一些额外时间来重写需要分析的class。Profile at startup
在被分析的jvm启动时,将指定的JProfiler Agent手动加载到该jvm。JProfiler GUI 将收集信息类型和策略等配置信息通过socket发送给JProfiler Agent,收到这些信息后该jvm才会启动。
在被分析的jvm 的启动参数增加下面内容:
语法: -agentpath:[path to jprofilerti library]
【注】: 语法不清楚没关系,JProfiler提供了帮助向导.
(图3)Prepare for profiling:
和Profile at startup的主要区别:被分析的jvm不需要收到JProfiler GUI 的相关配置信息就可以启动。Offline profiling
一般用于适用于不能直接调试线上的场景。Offline profiling需要将信息采集内容和策略(一些Trigger, Trigger请参考文章第五部分)打包成一个配置文件(config.xml),在线上启动该jvm 加载 JProfiler Agent时,加载该xml。那么JProfiler Agent会根据Trigger的类型会生成不同的信息。例如: heap dump; thread dump; method call record等
语法:
-agentpath:/home/2080/jprofiler8/bin/Linux-x64/libjprofilerti.so=offline,id=151,config=/home/2080/config.xml
【注】: config.xml中的每一个被分析的jvm的采集信息都有一个id来标识。
下面是使用了离线模式,并使用了每隔一秒dump heap 的Trigger:
四. JProfiler核心概念
Filter: 什么class需要被分析。分为包含和不包含两种类型的Filter。
(图4)Profiling Settings: 收据收集的策略:Sampling和 Instrumentation,一些数据采集细节可以自定义.
(图5)Triggers: 一般用于**offline**模式,告知JProfiler Agent 什么时候触发什么行为来收集指定信息.
(图6)Live memory: class/class instance的相关信息。 例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。
(图7)Heap walker: 对一定时间内收集的内存对像信息进行静态分析,功能强大且使用。包含对象的outgoing reference, incoming reference, biggest object等
(图8)CPU views: CPU消耗的分布及时间(cpu时间或者运行时间); 方法的执行图; 方法的执行统计(最大,最小,平均运行时间等)
(图9)Thread: 当前jvm所有线程的运行状态,线程持有锁的状态,可dump线程。
(图10)Monitors & locks: 所有线程持有锁的情况以及锁的信息
(图11)Telemetries: 包含heap, thread, gc, class等的趋势图(遥测视图)
五. 实践
为了方便实践,直接以JProfiler8自带的一个例子来帮助理解上面的相关概念。
JProfiler 自带的例子如下:模拟了内存泄露和线程阻塞的场景:
具体源码参考: /jprofiler install path/demo/bezier
(图12 )
(图13 Leak Memory 模拟内存泄露, Simulate blocking 模拟线程间锁的阻塞)
A1. 首先来分析下内存泄露的场景:(勾选图13中 Leak Memory 模拟内存泄露)
1. 在**Telemetries-> Memory**视图中你会看到大致如下图的场景(在看的过程中可以间隔一段时间去执行Run GC这个功能):看到下图蓝色区域,老生代在gc后(**波谷**)内存的大小在慢慢的增加(理想情况下,这个值应该是稳定的)
(图14)
在 Live memory->Recorded Objects 中点击**record allocation data**按钮,开始统计一段时间内创建的对象信息。执行一次**Run GC**后看看当前对象信息的大小,并点击工具栏中**Mark Current**按钮(其实就是给当前对象数量打个标记。执行一次Run GC,然后再继续观察;执行一次Run GC,然后再继续观察...。最后看看哪些对象在不断GC后,数量还一直上涨的。最后你看到的信息可能和下图类似
(图15 绿色是标记前的数量,红色是标记后的增量)在Heap walker中分析刚才记录的对象信息
(图16)
(图17)点击上图中实例最多的class,右键**Use Selected Instances->Reference->Incoming Reference**.
发现该Long数据最终是存放在**bezier.BeaierAnim.leakMap**中。
(图18)
在Allocations tab项中,右键点击其中的某个方法,可查看到具体的源码信息.
(图19)
【注】:到这里问题已经非常清楚了,明白了在图17中为什么哪些实例的数量是一样多,并且为什么内存在fullgc后还是回收不了(一个old 区的对象leakMap,put的信息也会进入old区, leakMap如回收不掉,那么该map中包含的对象也回收不掉)。
A2. 模拟线程阻塞的场景(勾选图13中Simulate blocking 模拟线程间锁的阻塞)
为了方便区分线程,我将Demo中的BezierAnim.java的L236的线程命名为test
public void start() {
thread = new Thread(this, "test");
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
正常情况下,如下图
(图20)
勾选了Demo中"Simulate blocking"选项后,如下图(注意看下下图中的状态图标), test线程block状态明显增加了。
(图21)
在**Monitors & locks->Monitor History**观察了一段时间后,会发现有4种发生锁的情况。
第一种:
AWT-EventQueue-0 线程持有一个Object的锁,并且处于Waiting状态。
图下方的代码提示出Demo.block方法调用了object.wait方法。这个还是比较容易理解的。
(图22)
第二种:
AWT-EventQueue-0占有了bezier.BezierAnim$Demo实例上的锁,而test线程等待该线程释放。
注意下图中下方的源代码, 这种锁的出现原因是Demo的blcok方法在AWT和test线程
都会被执行,并且该方法是synchronized.
(图23)
第三种和第四种:
test线程中会不断向事件Event Dispatching Thread提交任务,导致竞争java.awt.EventQueue对象锁。
提交任务的方式是下面的代码:repaint()和EventQueue.invokeLater
public void run() {
Thread me = Thread.currentThread();
while (thread == me) {
repaint();
if (block) {
block(false);
}
try {
Thread.sleep(10);
} catch (Exception e) {
break;
}
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
onEDTMethod();
}
});
}
thread = null;
}
(图24)
六. 最佳实践
- JProfiler都会对一些特殊操作给予提示,这时候最好仔细阅读下说明.
- "Mark Current"功能在某些场景很有效
- Heap walker一般是静态分析在Live memory->Recorder objects的对象信息,这些信息可能会被GC回收掉,导致Heap walker中什么也没有显示出来。这种现象是正常的。
- 可以才工具栏中Start Recordings配置一次性收集的信息
- Filter中include和exclude是有顺序的,注意使用下图**左下方**的**“Show Filter Tree”**来验证一下顺序
(图25)
七. 参考文献
JFR 与 JProfilter Jvmisualvm的更多相关文章
- java JFR
1. 参数: -XX:+UnlockCommercialFeatures -XX:+FlightRecorder 2. 运行命令: jcmd <PID> JFR.start name=te ...
- 运行java飞行记录器JFR(java flight recorder)
JFR 上面讲到的工具都是作为快速的查看诊断工具的.如果要深入分析问题,可以选择使用内置的Java飞行记录器:Java Mission Control. 转储JFR需要三步: 1. 创建一个包含了你自 ...
- Java性能调优:利用JFR生成性能日志
Java性能调优作为大型分布式系统提供高性能服务的必修课,其重要性不言而喻. 好的分析工具能起到事半功倍的效果,利用分析利器JMC.JFR,可以实现性能问题的准确定位. 本文主要阐述如何利用JFR生成 ...
- JFR 使用记录
进程的内存信息,可以使用jmap 和 jstack 等dump出文件,使用jhat 分析 dump 文件.不过比较简陋. 可以不停进程的方式有 JFR 或者taobao 开源组件. 本篇只记录JFR相 ...
- troubleshoot之:使用JFR解决内存泄露
目录 简介 一个内存泄露的例子 使用JFR和JMC来分析内存泄露 OldObjectSample 总结 简介 虽然java有自动化的GC,但是还会有内存泄露的情况.当然java中的内存泄露跟C++中的 ...
- troubleshoot之:使用JFR分析性能问题
目录 简介 GC性能事件 同步性能 IO性能 代码执行的性能 其他有用的event 简介 java程序的性能问题分析是一个很困难的问题.尤其是对于一个非常复杂的程序来说,分析起来更是头疼. 还好JVM ...
- 利用Java Flight Recorder(JFR)诊断timing及内存问题
Java Flight Recorder(JFR), 以下简称JFR,请注意这个只有Oracle JDK 1.7(7u40)或以上版本才有, OpenJDK木有这东西. 启用: Java命令行启动参数 ...
- 通过 JFR 与日志深入探索 JVM - TLAB 原理详解
全系列目录:通过 JFR 与日志深入探索 JVM - 总览篇 什么是 TLAB? TLAB(Thread Local Allocation Buffer)线程本地分配缓存区,这是一个线程专用的内存分配 ...
- Java JFR 民间指南 - 事件详解 - jdk.ObjectAllocationSample
对象分配采样:jdk.ObjectAllocationSample 引入版本:Java 16 相关 ISSUE:Introduce JFR Event Throttling and new jdk.O ...
随机推荐
- 【python】单下划线与双下划线的区别
Python 用下划线作为变量前缀和后缀指定特殊变量. _xxx 不能用'from moduleimport *'导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 以单下划线开头(_f ...
- 【selenium】HTMLTestRunner测试报告生成
__author__ = 'Administrator' #coding=utf-8 from selenium import webdriver from selenium.webdriver.co ...
- Servlet容器请求处理
红色部分为本章所述模块 所讲述的请求流程模块,大家已经很清楚了.那怎么给大家去讲的更清晰,大家理解的更容易呢?当然是,带着问题去学习,吸收或许会更快些啦.:) 开篇之前,给大家提以下几个问题,这些问题 ...
- 【转】C# 根据当前时间获取,本周,本月,本季度等时间段 .Net中Exception
1 DateTime dt = DateTime.Now; //当前时间 2 3 DateTime startWeek = dt.AddDays(1 - Convert.ToInt32(dt.DayO ...
- bzoj4697: 猪
Description 红学姐和黄学长是好朋友.红学姐有一只宠物,叫魔法猪.黄学长也有一只宠物,叫小奇.有 n 个猪圈排成一排 ,魔法猪藏在某个猪圈中.为了找到魔法猪,小奇会向你询问一些猪圈中猪的个数 ...
- java正则表达式学习
1.简单认识正则: public class Test { public static void main(String[] args) { //简单认识正则 p("abc".ma ...
- 【转载】 java利用snmp4j包来读取snmp协议数据(Manager端)
https://www.cnblogs.com/xdp-gacl/p/4187089.html http://doc.okbase.net/yuanfy008/archive/265663.html ...
- 廖雪峰Java1-2Java程序基础-3整数运算
1.四则运算规则 int i =(100 + 200) * (99 -88);//3300 int n = i + 9;//3309 //除法结果为整数 int q = n / 100;//33 // ...
- 1006 Sign In and Sign Out (25 分)
1006 Sign In and Sign Out (25 分) At the beginning of every day, the first person who signs in the co ...
- mac 安装 nginx
我是用root用户装的 1.先安装PCRE库 可以在这里下载最新版,我这里使用的是8.33的版本然后在终端执行下面的命令. cd ~/Download tar xvzf pcre-8.33.tar.g ...