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 ...
随机推荐
- ASP.NET 实现伪静态网页方法
方法一:利用Httphandler实现URL重写(伪URL及伪静态) 我们有时候会见到这样的地址:“http://www.huoho.com/show-12-34.html”,你或许认为在站点服务器根 ...
- Mybatis常见面试题 一
1.#{}和${}的区别是什么? 注:这道题是面试官面试我同事的. 答:${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静 ...
- 《Wrox.Professional.Hadoop.Solutions》中文目录全稿
前言:最近有朋友给推荐一本书,英文原版<Wrox.Professional.Hadoop.Solutions>,感觉很好打算翻译成中文,共享给朋友,时间关系,不知能否成行,先干着吧.以下部 ...
- 大数据应用之HBase数据插入性能优化实测教程
引言: 大家在使用HBase的过程中,总是面临性能优化的问题,本文从HBase客户端参数设置的角度,研究HBase客户端数据批量插入性能优化的问题.事实胜于雄辩,数据比理论更有说服力,基于此,作者设计 ...
- 获取 user-agents
user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0. ...
- java web程序 登陆验证页面 4个页面人性化设置
到这里,快期末考试了,老师不讲课,我心里有苦不想说,也许没有考虑到老师的感受,让老师难堪了 但是我的行为已不再是我可以做的了.不可能了,我只是职业性的机械的做事了. 思路: 1.第一个是form表单, ...
- 针对IE6浏览器下,zoom:1的问题
一.css代码如下: .message .con .word {font-size:14px;color:#333333; border-radius:3px; padding:10px;border ...
- tsql 循环id读取
declare @IDList as varchar(max) declare @ID as int declare @i as int set @IDList='' )) + ',' from ta ...
- python爬虫rp+bs4
一.开发环境 Beautiful Soup 4.4.0 文档: http://beautifulsoup.readthedocs.io/zh_CN/latest/#id28 Requests : ht ...
- OpenGL chapter5 基础纹理
Chapter5 基础纹理 Contents: ==================================================== | 任务 | 使用的函数 ========== ...