性能调优必备神器-Jprofiler解析

Posted 测试界的飘柔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能调优必备神器-Jprofiler解析相关的知识,希望对你有一定的参考价值。

01 安装JProfiler

从http://www.ej-technologies.com/下载5.1.2并申请试用序列号

02 主要功能简介

1.内存剖析 Memory profiler

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

所有对象

显示类或在状况统计和尺码信息堆上所有对象的包。你可以标记当前值并显示差异值。

记录对象 Record objects

显示类或所有已记录对象的包。你可以标记出当前值并且显示差异值。

分配访问树 Allocation call tree

显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。

分配热点 Allocation hot spots

显示一个列表,包括方法、类、包或分配已选类的J2EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。

2.堆遍历 Heap walker

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

类 Classes

显示所有类和它们的实例。

分配 Allocations

为所有记录对象显示分配树和分配热点。

索引 References

为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示能。还能提供合并输入视图和输出视图的功能。

数据 Data

为单个对象显示实例和类数据。

时间 Time

显示一个对已记录对象的解决时间的柱状图。

3.CPU 剖析 CPU profiler

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

访问树 Call tree

显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC,JMS和JNDI服务请求都被注释在请求树中。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。

热点 Hot spots

显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请求以及按照URL请求来进行计算。

访问图 Call graph

显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。

4.线程剖析 Thread profiler

对线程剖析,JProfiler提供以下视图:

线程历史 Thread history

显示一个与线程活动和线程状态在一起的活动时间表。

线程监控 Thread monitor

显示一个列表,包括所有的活动线程以及它们目前的活动状况。

死锁探测图表 Deadlock Detection

显示一个包含了所有在JVM里的死锁图表。

目前使用的监测器 Current monitor useage

显示目前使用的监测器并且包括它们的关联线程。

历史检测记录 History usage history

显示重大的等待事件和阻塞事件的历史记录。

监测使用状态 Monitor usage statistics

显示分组监测,线程和监测类的统计监测数据。

5.VM 遥感勘测技术 VM telemetry

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

堆 Heap

显示一个堆的使用状况和堆尺寸大小活动时间表。

记录的对象 Recorded objects

显示一张关于活动对象与数组的图表的活动时间表。

垃圾回收 Garbage collector

显示一张关于垃圾回收活动的活动时间表。

类 Classes

显示一个与已装载类的图表的活动时间表。

线程 Threads

显示一个与动态线程图表的活动时间表。

03 配置

一、 JProfiler’s start center

在安装完毕后打开会有一个页面向导

使用 JProfiler’s start center,你可以创建新的会话,编辑已有会话或者打开已保存的会话。

在菜单中选择 start center,也可以打开这个向导页面、有以下四个标签:Open session、New session、Convert session和 Open snapshot。

我们可以先看第一个——

1.1 Open session

在 start center 中,打开 Open session标签,在窗口中显示所有预先定义的会话,如上图:

你可以选择一个会话,点击 OK来打开预定义的会话。

会话也可以被修改、复制、删除和排序。

也可以菜单中选择 session –> Open session,打开 Open session窗口。

1.2 New session

会话能够通过两种途径创建:

第一种:人工配置:使用New session]按钮手工配置一个新的session,配置完成后,开始运行(如何配置详见下面)

第二种:通过集成向导:使用集成向导上的三个按钮:[New server integration] 、[New remote integration] 和 [New applet integration] 。

会话创建完成后可以立即运行。在 Open session标签页中能够看到新建的会话。

1.2.1 New Session(人工配置创建)

点击 New Session按钮,显示Application Settings窗体

此页面也可以在菜单中选择 Session–>New Session,打开 New Session 窗体

1.2.2 New server integration(通过集成向导创建)

点击 New server integration按钮,打开集成向导,引导你将 JProfiler与本地或远程的应用服务器进行集成。

步骤:

第一步、选择需要集成的应用服务器。

如果你所使用的应用服务器不在列表中,则选择“Generic application sever ”,并点击“下一步”;如图:

第二步、 选择要集成的应用服务器地址

选择要集成的应用服务器是本地的,还是远程的。如果你选择远程计算机,在选择的计算机上必须安装 JProfiler,并选择远程计算机的操作系统,点击“下一步”如图:

第三步:选择 JVM提供商,版本和模式(也就是指定jdk的版本)

当你选择 jdk版本时,系统会自动选择推荐的模式

第四步、选择 Jprofiler 启动界面方式。

在开发环境,建立选择第一项,很容易修改监测设置;如果你选择不等待,在启动应用服务器之前要先配置监测设置。VM参数依赖于 JProfiler配置文件的位置;配置文件要与远程计算机上的保持同步。

wait for a connection from the Jprofiler GUI :监控时 tomcat 需要由 JProfiler来启动

startup immediately,connect later with the JProfiler GUI:立即启动,稍后与JProfiler 页面连接

profile offline ,JProfiler GUI cannot connect :在脱机情况下,jprofiler gui 无法连接

我选择第一个立即启动稍后连接(有兴趣的可以都尝试一下)

第五步:选择 Tomcat 启动脚本文件路径

第六步:指定JPfrofiler的端口(选择监测的连接端口)

JProfiler GUI 前端通过指定的端口连接被监测的 JVM,默认端口为 8849。请不要选择常用的端口,如 80或 8080。

第七步:检查需求通知

集成向导完成配置,所有必须的信息会进行通知,请检查,点击下一步。如图:

第八步:完成是否立即运行

这里就新建一个session完毕。配置完毕后你会在Open session 中看到你配置的内容。选择启动、因为在第四步中选择了第一种。所以会出现


选择第二个、之后会出现

点击ok、则启动成功了!

*这里有个问题:

在第四步中如果选择了第二种。那么无论是tomcat启动没有启动JProfiler都启动不了抛出一个Connection error的错

待排查

而在第四步选择第三种方式时。tomcat有没有启动JProfiler抛上面相同的错误。同上面。

待排查

1.3 Open snapshot

可以通过打开*.jps文件来打开原来保存过的 session

二、 管理 session(不做详解)

2.1 Application settings dialog

这个页面可以是在创建session时管理,也可在open session右键编辑管理(如前面人工配置图片)
session 名称 session类型等根据需要自定义

三、内存视图

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

Java性能分析神器-JProfiler详解(转)

前段时间在给公司项目做性能分析,从简单的分析Log(GC log, postgrep log, hibernate statitistic),到通过AOP搜集软件运行数据,再到PET测试,感觉时间花了不少,性能也有一定的提升,但总感觉像是工作在原始时代,无法简单顺畅,又无比清晰的获得想要的结果。遂花费了一定的时间,从新梳理学习了一下之前用过的关于jvm调优和内存分析的各种工具,包括JDK自带的jps, jstack, jmap, jconsole,以及IBM的HeapAnalyzer等,这些工具虽然提供了不少功能,但其可用度,便捷度,远没达到IntelliJ之于Java开发那种地步。在偶然情况下,在云栖社区上发现有人推荐Jprofiler,装上使用版一用,发现果然是神器,特此推荐给大家。先声明,这个软件是商用的,网上有很多关于lisence的帖子,我这里转发,但是绝不推荐大家用破解版!

[email protected]#36573-fdkscp15axjj6#25257
[email protected]#5481-ucjn4a16rvd98#6038
[email protected]#99016-hli5ay1ylizjj#27215
[email protected]#40775-3wle0g1uin5c1#0674
[email protected]#7009-14frku31ynzpfr#20176
[email protected]#49604-1jfe58we9gyb6#5814
[email protected]#25531-1qcev4yintqkj#23927
[email protected]#96496-1qsu1lb1jz7g8w#23479
[email protected]#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核心概念

  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();                    }                });            }            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: http://resources.ej-technologies.com/jprofiler/help/doc/index.html
    2. JVMTI: http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html

    3.  






















































































以上是关于性能调优必备神器-Jprofiler解析的主要内容,如果未能解决你的问题,请参考以下文章

Java性能分析神器-JProfiler详解(转)

Java性能分析神器-JProfiler详解(转)

[转帖]Java性能分析神器-JProfiler详解(转)

性能调优工具jprofiler安装详解

使用Jprofiler+jmeter进行JVM性能调优

性能分析工具jprofiler的详细使用说明(程序员必学)