技巧:Java性能分析神器-JProfiler详解(一)

优采云 发布时间: 2022-11-02 07:12

  技巧:Java性能分析神器-JProfiler详解(一)

  前段时间在为公司的项目做性能分析,从简单的Log分析(GC日志,postgrep日志,hibernate统计),到通过AOP采集软件运行数据,再到PET测试。感觉花了很多时间,性能也有一定的提升,但总感觉像是在原创时代工作,不可能以简单流畅、极其清晰的方式获得想要的效果。花了一些时间整理学习了之前用过的各种jvm调优和内存分析的工具,包括jps、jstack、jmap、jconsole,JDK自带的,还有IBM的HeapAnalyzer等。虽然这些工具都提供了很多的功能,但它的可用性和便利性远非 IntelliJ 用于 java 开发的水平。偶尔,在云栖社区发现有人推荐Jprofiler,安装版本使用,发现是神器,特此推荐给大家。首先,我声明这个软件是用于商业用途的。网上有很多关于 lisence 的帖子。我会在这里转发,但我绝对不会推荐大家使用破解版!

  #36573-fdkscp15axjj6#25257

  #5481-ucjn4a16rvd98#6038

  #99016-hli5ay1ylizjj#27215

  #40775-3wle0g1uin5c1#0674

  #7009-14frku31ynzpfr#20176

  #49604-1jfe58we9gyb6#5814

  #25531-1qcev4yintqkj#23927

  #96496-1qsu1lb1jz7g8w#23479

  #20948-11amlvg181cw0p#171159

  然后,转到云栖上的一个文章,然后慢慢开始我们的Jprofiler之旅。

  1.什么是JProfiler

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

  其特点:

  2.数据采集

  Q1。既然 JProfiler 是一个性能瓶颈分析工具,那么这些分析的相关数据是从哪里来的呢?

  Q2。JProfiler 如何采集和显示这些数据?

  (图2)

  A1。分析的数据主要来自以下两部分

  1.部分分析接口来自jvm**JVMTI**(JVM Tool Interface),JDK必须>=1.6

  JVMTI 是一个基于事件的系统。分析代理库可以为不同的事件注册处理函数。然后它可以启用或禁用选定的事件

  例如:对象生命周期、线程生命周期等信息

  2.部分来自instruments类(可以理解为类的重写,增加了JProfiler相关的统计功能)

  例如:方法执行时间、次数、方法栈等信息

  A2。数据采集​​原理如图2所示

  1.用户在JProfiler GUI中发出监控命令(通常通过点击按钮)

  2. JProfiler GUI JVM通过socket(默认端口8849)向被分析jvm中的JProfile Agent发送指令。

  3. JProfiler Agent(如果不了解Agent,请看文章Part 3“启动模式”)收到指令后,将指令转换成相关事件或需要监控的指令,注册到JVMTI 或者直接让JProfiler Agent JVMTI 执行一个功能(比如dump jvm内存)

  4、JVMTI根据注册的事件采集当前jvm的信息。例如:线程的生命周期;jvm的生命周期;类的生命周期;对象实例的生命周期;堆内存的实时信息等

  5. JProfiler Agent将采集好的信息保存在**memory**中,并按照一定的规则进行统计(如果所有数据都发送到JProfiler GUI,对被分析的应用网络会有比较大的影响)

  6. 返回 JProfiler GUI Socket。

  7. JProfiler GUI Socket将接收到的信息返回给JProfiler GUI Render

  8. JProfiler GUI Render 渲染最终显示效果

  3.data采集方法及启动方式

  A1。JProfier采集 方法分为两种:Sampling (sample采集) 和 Instrumentation

  注意:JProfiler 本身并没有指明 采集 类型的数据,这里的 采集 类型是 采集 类型用于方法调用。因为JProfiler的大部分核心功能都依赖于方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。

  A2:启动模式:

  四。JProfiler 核心概念

  过滤器:需要分析什么类。有两种类型的过滤器收录和不收录。

  (图4)

  Profiling Settings: Receipt 采集 strategy: Sampling and Instrumentation,一些数据采集细节可以自定义。

  (图5)

  Triggers:一般用在**offline**模式下,告诉JProfiler Agent什么时候触发什么行为来采集指定信息。

  (图 6)

  实时内存:关于类/类实例的信息。比如对象的数量和大小,对象创建的方法执行栈,对象创建的热点。

  

  (图 7)

  Heap walker:静态分析一定时间内采集到的内存对象信息,功能强大,好用。传出引用、传入引用、最大对象等。

  (图 8)

  CPU视图:CPU消耗分布和时间(cpu时间或运行时间);方法执行图;方法执行统计(最大值、最小值、平均运行时间等)

  (图 9)

  线程:当前jvm所有线程的运行状态,持有锁的线程的状态,可以dump线程。

  (图 10)

  Monitors & locks:所有线程持有锁和锁信息

  (图 11)

  遥测:趋势图(遥测视图),包括堆、线程、gc、类等。

  五、实践

  为了方便实践,使用JProfiler8自带的一个例子来帮助理解上述相关概念。

  JProfiler 自带的例子如下: 模拟内存泄漏和线程阻塞的场景:

  具体源码参考:/jprofiler install path/demo/bezier

  (图 12)

  (图13Leak Memory模拟内存泄漏,模拟阻塞模拟线程间锁的阻塞)

  A1。首先我们来分析一下内存泄露的场景:(勾选图13中的Leak Memory来模拟内存泄露)

  1、在**Telemetries->Memory**视图中,会看到下图的场景(查看过程中可以间隔执行Run GC功能):见下图蓝色区域,老年代 gc (**trough**) 后内存大小缓慢增加(理想情况下,这个值应该是稳定的)

  (图 14)

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

  (图15绿色为标注前的数量,红色为标注后的增量)

  分析刚刚记录在Heap walker中的对象信息

  (图 16)

  (图 17)

  点击上图中实例最多的类,右键**Use Selected Instances->Reference->Incoming Reference**。

  发现Long数据最终存放在**bezier.BeierAnim.leakMap**中。

  (图 18)

  在 Allocations 选项卡中,右键单击其中一种方法以查看特定的源代码信息。

  (图 19)

  【注】:此时问题已经很清楚了。明白为什么图17的实例数一样,为什么fullgc后内存无法回收(老区的一个对象leakMap,put的信息也会进入老区,如果leakMap无法回收,地图中收录的对象无法回收)。

  A2。模拟线程阻塞的场景(勾选图13中的模拟阻塞,模拟线程间锁的阻塞)

  为了方便区分线程,我将Demo中BezierAnim.java的L236的线程命名为test

  public void start() {

thread = new Thread(this, "test");

thread.setPriority(Thread.MIN_PRIORITY);

thread.start();

}

<p>

</p>

  正常情况下,如下图所示

  (图 20)

  在Demo中勾选“模拟阻塞”选项后,如下图(注意下图中的状态图标),测试线程的阻塞状态明显增加。

  (图 21)

  观察**Monitors &amp; locks-&gt;Monitor History**一段时间后,你会发现出现锁的情况有4种。

  首先:

  AWT-EventQueue-0 线程持有对象锁,处于等待状态。

  下图代码表示Demo.block方法调用了object.wait方法。这还是比较容易理解的。

  (图 22)

  第二:

  AWT-EventQueue-0 持有 bezier.BezierAnim$Demo 实例上的锁,测试线程等待线程释放它。

  注意下图底部的源代码。这个锁的原因是Demo的blcok方法在AWT和测试线程中。

  将被执行并且方法被同步。

  (图 23)

  第三和第四:

  测试线程会继续向 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)

  6. 最佳实践 JProfiler 会给出一些特殊操作的提示。这时,最好仔细阅读说明。“标记当前”功能在某些场景下非常有效。Heap walker 一般静态分析 Live memory-&gt;Recorder objects 中的对象信息,这些信息可能会被 GC 回收,导致 Heap walker 中什么也不显示。这种现象是正常的。您可以在工具栏中的“开始录制”工具栏中配置一次采集的信息。Filter 中的 include 和 exclude 是按顺序排列的。请使用下图左下方的“显示过滤树”来验证顺序。

  (图 25) 七。参考 JProfiler 助手:

  JVMTI:

  实用文章:文章*敏*感*词*大全

  

  文章*敏*感*词*集合

  2022年1月11日

  

  文章*敏*感*词*具有多种功能在线,一键分享修改等功能,好评!如果你是网站编辑,你可以帮忙协助修改文章,成为自己的想法开启新的文章探索,如果你能看到更多文章好的资源,你可以去一键修改,一键生成,这样帮助我们打开更多的资源整合,分享自己的文章,还有一键排版功能,一键分享功能,还可以检测原创问题, 合作伙伴可以来这里开启更多玩法!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线