JProfiler使用说明

一.JProfiler是什么

JProfiler是由ej-technologies GmbH公司开发的一款性能瓶颈分析工具(该公司还开发部署工具)。
其特点:

  • 使用方便
  • 界面操作友好
  • 对被分析的应用影响小
  • CPU,Thread,Memory分析功能尤其强大
  • 支持对jdbc,noSql, jsp, servlet, socket等进行分析
  • 支持多种模式(离线,在线)的分析
  • 跨平台 _2014_10_06_7_54_34_PM (图1)

二.数据采集

Q1. JProfiler既然是一款性能瓶颈分析工具,这些分析的相关数据来自于哪里?
Q2. JProfiler是怎么将这些数据收集并展现的?

_2014_10_06_3_09_15_PM
(图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提供了帮助向导.
    _2014_10_06_4_15_42_PM
    (图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:
    _2014_10_06_9_07_56_PM

四. JProfiler核心概念

  1. Filter: 什么class需要被分析。分为包含和不包含两种类型的Filter。
    _2014_10_06_4_32_15_PM
    (图4)

  2. Profiling Settings: 收据收集的策略:Sampling和 Instrumentation,一些数据采集细节可以自定义.
    _2014_10_06_4_34_28_PM
    (图5)

  3. Triggers: 一般用于**offline**模式,告知JProfiler Agent 什么时候触发什么行为来收集指定信息.
    _2014_10_06_4_37_47_PM
    (图6)

  4. Live memory: class/class instance的相关信息。 例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。
    _2014_10_06_4_56_09_PM
    (图7)

  5. Heap walker: 对一定时间内收集的内存对像信息进行静态分析,功能强大且使用。包含对象的outgoing reference, incoming reference, biggest object等
    _2014_10_06_4_55_39_PM
    (图8)

  6. CPU views: CPU消耗的分布及时间(cpu时间或者运行时间); 方法的执行图; 方法的执行统计(最大,最小,平均运行时间等)
    _2014_10_06_4_54_48_PM
    (图9)

  7. Thread: 当前jvm所有线程的运行状态,线程持有锁的状态,可dump线程。
    _2014_10_06_4_54_07_PM
    (图10)

  8. Monitors & locks: 所有线程持有锁的情况以及锁的信息
    _2014_10_06_4_59_15_PM
    (图11)

  9. Telemetries: 包含heap, thread, gc, class等的趋势图(遥测视图)

五. 实践

为了方便实践,直接以JProfiler8自带的一个例子来帮助理解上面的相关概念。
JProfiler 自带的例子如下:模拟了内存泄露和线程阻塞的场景:
具体源码参考: /jprofiler install path/demo/bezier

_2014_10_06_5_06_49_PM
(图12 )

_2014_10_06_5_09_43_PM
(图13 Leak Memory 模拟内存泄露, Simulate blocking 模拟线程间锁的阻塞)

A1. 首先来分析下内存泄露的场景:(勾选图13中 Leak Memory 模拟内存泄露)
1. 在**Telemetries-> Memory**视图中你会看到大致如下图的场景(在看的过程中可以间隔一段时间去执行Run GC这个功能):看到下图蓝色区域,老生代在gc后(**波谷**)内存的大小在慢慢的增加(理想情况下,这个值应该是稳定的)
_2014_10_06_5_18_56_PM
(图14)

  1. 在 Live memory->Recorded Objects 中点击**record allocation data**按钮,开始统计一段时间内创建的对象信息。执行一次**Run GC**后看看当前对象信息的大小,并点击工具栏中**Mark Current**按钮(其实就是给当前对象数量打个标记。执行一次Run GC,然后再继续观察;执行一次Run GC,然后再继续观察...。最后看看哪些对象在不断GC后,数量还一直上涨的。最后你看到的信息可能和下图类似
    _2014_10_06_5_26_41_PM
    (图15 绿色是标记前的数量,红色是标记后的增量)

  2. 在Heap walker中分析刚才记录的对象信息
    _2014_10_06_5_28_00_PM
    (图16)
    _2014_10_06_5_29_11_PM
    (图17)

  3. 点击上图中实例最多的class,右键**Use Selected Instances->Reference->Incoming Reference**.
    发现该Long数据最终是存放在**bezier.BeaierAnim.leakMap**中。
    _2014_10_06_5_31_54_PM
    (图18)

在Allocations tab项中,右键点击其中的某个方法,可查看到具体的源码信息.
_2014_10_06_5_36_08_PM
(图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();
        }

正常情况下,如下图
_2014_10_06_5_50_25_PM
(图20)

勾选了Demo中"Simulate blocking"选项后,如下图(注意看下下图中的状态图标), test线程block状态明显增加了。
_2014_10_06_5_53_19_PM
(图21)

在**Monitors & locks->Monitor History**观察了一段时间后,会发现有4种发生锁的情况。

第一种:
AWT-EventQueue-0 线程持有一个Object的锁,并且处于Waiting状态。

图下方的代码提示出Demo.block方法调用了object.wait方法。这个还是比较容易理解的。 
_2014_10_06_6_15_59_PM
(图22)

第二种:
AWT-EventQueue-0占有了bezier.BezierAnim$Demo实例上的锁,而test线程等待该线程释放。

注意下图中下方的源代码, 这种锁的出现原因是Demo的blcok方法在AWT和test线程
都会被执行,并且该方法是synchronized.
_2014_10_06_6_11_20_PM
(图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;
        }

_2014_10_06_6_20_05_PM
(图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”**来验证一下顺序 _2014_10_17_3_41_18_PM (图25)

七. 参考文献

  1. JProfiler helper: http://resources.ej-technologies.com/jprofiler/help/doc/index.html
  2. JVMTI: http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html

补充----------------------------------------------------------------------------------------------------------------------------

模块介绍

  模块的介绍主要是对JProfiler的具体功能进行介绍,部分内容参照自博客:JProfiler 解决 Java 服务器的性能跟踪,如果读者英文阅读能力比较强也可以在工具栏上边点击Help,直接阅读英文帮助,下文部分内容亦是参照自英文API。

  

3.2.1 内存视图 Memory Views

  JProfiler的内存视图部分可以提供动态的内存使用状况更新视图和显示关于内存分配状况信息的视图。所有的视图都有几个聚集层并且能够显示现有存在的对象和作为垃圾回收的对象。

  

  • 所有对象 All Objects
    显示类或在状况统计和尺码信息堆上所有对象的包。你可以标记当前值并显示差异值。
  • 记录对象 Record Objects 
    显示类或所有已记录对象的包。你可以标记出当前值并且显示差异值。
  • 分配访问树 Allocation Call Tree 
    显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
  • 分配热点 Allocation Hot Spots 
    显示一个列表,包括方法、类、包或分配已选类的J2EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。
  • 类追踪器 Class Tracker 
    类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。
3.2.2 堆遍历 Heap Walker

  在JProfiler的堆遍历器(Heap Walker)中,你可以对堆的状况进行快照并且可以通过选择步骤下寻找感兴趣的对象。堆遍历器有五个视图:

  

  • 类 Classes 
    显示所有类和它们的实例,可以右击具体的类"Used Selected Instance"实现进一步跟踪。
  • 分配 Allocations 
    为所有记录对象显示分配树和分配热点。
  • 索引 References 
    为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和输出视图的功能。
  • 时间 Time 
    显示一个对已记录对象的解决时间的柱状图。
  • 检查 Inspections 
    显示了一个数量的操作,将分析当前对象集在某种条件下的子集,实质是一个筛选的过程。
  • 图表 Graph
    你需要在references视图和biggest视图手动添加对象到图表,它可以显示对象的传入和传出引用,能方便的找到垃圾收集器根源。

  tips:在工具栏点击"Go To Start"可以使堆内存重新计数,也就是回到初始状态。

  

3.2.3 CPU 视图 CPU Views

  JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。CPU视图部分包括:

  

  • 访问树 Call Tree 
    显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC,JMS和JNDI服务请求都被注释在请求树中。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。
  • 热点 Hot Spots 
    显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请求以及按照URL请求来进行计算。
  • 访问图 Call Graph 
    显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。
  • 方法统计 Method Statistis
    显示一段时间内记录的方法的调用时间细节。
3.2.4 线程视图 Thread Views

  JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析,JProfiler提供以下视图:

  

  • 线程历史 Thread History 
    显示一个与线程活动和线程状态在一起的活动时间表。
  • 线程监控 Thread Monitor 
    显示一个列表,包括所有的活动线程以及它们目前的活动状况。
  • 线程转储 Thread Dumps 
    显示所有线程的堆栈跟踪。
3.2.5 监控器视图 Monitor Views

  JProfiler提供了不同的监控器视图,如下所示:

  

  • 当前锁定图表 Current Locking Graph 
    显示JVM中的当前锁定情况。
  • 当前监视器 Current Monitors 
    显示当前正在等待或阻塞中的线程操作。
  • 锁定历史图表 Locking History Graph 
    显示记录在JVM中的锁定历史。
  • 监控器历史 Monitor History 
    显示等待或者阻塞的历史。
  • 监控器使用统计 Monitor Usage Statistics 
    计算统计监控器监控的数据。
3.2.6 VM遥感勘测技术视图 VM Telemetry Views

  观察JVM的内部状态,JProfiler提供了不同的遥感勘测视图,如下所示:

  

  • 内存 Memory 
    显示堆栈的使用状况和堆栈尺寸大小活动时间表。
  • 记录的对象 Recorded Objects 
    显示一张关于活动对象与数组的图表的活动时间表。
  • 记录的生产量 Recorded Throughput 
    显示一段时间累计的JVM生产和释放的活动时间表。
  • 垃圾回收活动 GC Activity
    显示一张关于垃圾回收活动的活动时间表。
  • 类 Classes 
    显示一个与已装载类的图表的活动时间表。
  • 线程 Threads 
    显示一个与动态线程图表的活动时间表。
  • CPU负载 CPU Load 
    显示一段时间中CPU的负载图表。

3.3 使用心得

3.3.1 看内存图

  

  Key 1,把目光集中到上图的蓝色部分,可以发现每次内存上升到一个峰值之后便会下跌,这个动作其实就是GC在回收内存,并且上升和回收的幅度大抵相同,如果你发现内存图中可用内存随着时间的流逝一直在上升而没有GC回收的动作,那么你就要怀疑它是否存在着内存泄露了;

  Key 2,当你怀疑内存泄露时,可以在Memory Views视图Mark Current Values,然后在一段时间后F4,找出那个没有被释放掉的异常类对其进行深一步的追踪;

  Key 3,追踪主要使用Heap Walker的各项功能,各功能具体能做何种分析请复习章节3.1直观认识3.2.2 堆遍历 Heap Walker


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值