Java性能分析神器-JProfiler详解(转)
前段时间在给公司项⽬做性能分析,从简单的分析Log(GC log, postgrep log,  statitistic),到通过AOP搜集软件运⾏数据,再到PET,感觉时间花了不少,性能也有⼀定的提升,但总感觉像是⼯作在原始时代,⽆法简单顺畅,⼜⽆⽐清晰的获得想要的结果。遂花费了⼀定的时间,从新梳理学习了⼀下之前⽤过的关于jvm调优和内存分析的各种⼯具,包括JDK⾃带的jps, jstack, jmap, jconsole,以及IBM的HeapAnalyzer等,这些⼯具虽然提供了不少功能,但其可⽤度,便捷度,远没达到IntelliJ之于开发那种地步。在偶然情况下,在云栖社区上发现有⼈推荐Jprofiler,装上使⽤版⼀⽤,发现果然是神器,特此推荐给⼤家。先声明,这个软件是商⽤的,⽹上有很多关于lisence的帖⼦,我这⾥转发,但是绝不推荐⼤家⽤破解版!
L-Larry_Lau@163#36573-fdkscp15axjj6#25257
L-Larry_Lau@163#5481-ucjn4a16rvd98#6038
L-Larry_Lau@163#99016-hli5ay1ylizjj#27215
L-Larry_Lau@163#40775-3wle0g1uin5c1#0674
L-Larry_Lau@163#7009-14frku31ynzpfr#20176
L-Larry_Lau@163#49604-1jfe58we9gyb6#5814
L-Larry_Lau@163#25531-1qcev4yintqkj#23927
L-Larry_Lau@163#96496-1qsu1lb1jz7g8w#23479
L-Larry_Lau@163#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请参考⽂章第五部分)打包成⼀个配置⽂件(l),在线上启动该jvm 加载 JProfiler Agent时,加载该xml。那么JProfiler Agent会根据Trigger的类型会⽣成不同的信息。例如: heap dump; thread dump; method call record等
语法:
-agentpath:/home/2080/jprofiler8/bin/-x64/libjprofilerti.so=offline,id=151,config=/home/l
【注】: l中的每⼀个被分析的jvm的采集信息都有⼀个id来标识。
下⾯是使⽤了离线模式,并使⽤了每隔⼀秒dump heap 的Trigger:
四. JProfiler核⼼概念
1. Filter: 什么class需要被分析。分为包含和不包含两种类型的Filter。
(图4)
2. Profiling Settings: 收据收集的策略:Sampling和 Instrumentation,⼀些数据采集细节可以⾃定义.
(图5)
3. Triggers: ⼀般⽤于**offline**模式,告知JProfiler Agent 什么时候触发什么⾏为来收集指定信息.
(图6)
4. Live memory: class/class instance的相关信息。例如对象的个数,⼤⼩,对象创建的⽅法执⾏栈,对象创建的热点。
(图7)
5. Heap walker: 对⼀定时间内收集的内存对像信息进⾏静态分析,功能强⼤且使⽤。包含对象的outgoing reference, incoming reference,
biggest object等
(图8)
6. CPU views: CPU消耗的分布及时间(cpu时间或者运⾏时间); ⽅法的执⾏图; ⽅法的执⾏统计(最⼤,最⼩,平均运⾏时间等)
(图9)
7. Thread: 当前jvm所有线程的运⾏状态,线程持有锁的状态,可dump线程。
(图10)
8. Monitors & locks: 所有线程持有锁的情况以及锁的信息
(图11)
9. 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)
1. 在 Live memory->Recorded Objects 中点击**record allocation data**按钮,开始统计⼀段时间内创建的对象信息。执⾏⼀次**Run
GC**后看看当前对象信息的⼤⼩,并点击⼯具栏中**Mark Current**按钮(其实就是给当前对象数量打个标记。执⾏⼀次Run GC,然后再继续观察;执⾏⼀次Run GC,然后再继续观察...。最后看看哪些对象在不断GC后,数量还⼀直上涨的。最后你看到的信息可能和下图类似
(图15 绿⾊是标记前的数量,红⾊是标记后的增量)
2. 在Heap walker中分析刚才记录的对象信息
(图16)
(图17)
3. 点击上图中实例最多的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();
}
java源码阅读工具
});
}
thread = null;
}
(图24)
六. 最佳实践
1. JProfiler都会对⼀些特殊操作给予提⽰,这时候最好仔细阅读下说明.
2. "Mark Current"功能在某些场景很有效
3. Heap walker⼀般是静态分析在Live memory->Recorder objects的对象信息,这些信息可能会被GC回收掉,导致Heap walker中什么
也没有显⽰出来。这种现象是正常的。
4. 可以才⼯具栏中Start Recordings配置⼀次性收集的信息
5. Filter中include和exclude是有顺序的,注意使⽤下图**左下⽅**的**“Show Filter Tree”**来验证⼀下顺序  (图25)
七. 参考⽂献
1. JProfiler helper:
2.
3.

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。