异步和远程请求跟踪
异步执行任务是一种常见的做法,无论是在纯Java代码中,还是在响应式框架中更是如此。 在你的源文件中相邻的代码现在在两个或多个不同的线程上执行。对于调试和分析,这些线程变化带来了两个问题: 一方面,不清楚一个被调用的操作成本有多昂贵。另一方面,一个成本昂贵的操作无法回溯到导致其执行的代码。
对此,JProfiler根据调用是否停留在同一个JVM中,提供了不同的解决方案。如果异步执行发生在与其调用者同一个JVM中, "内联异步执行"调用树分析会计算出一个包含调用点以及执行点的单个调用树。 如果向远程JVM发出请求,则调用树中包含指向调用点和执行点的超链接, 这样你可以在不同的JProfiler顶层窗口之间无缝地双向导航,这些窗口显示了相关JVM的分析会话。
启用异步和远程请求跟踪
异步机制可以用多种方式实现,在单独的线程上或在不同的JVM中启动任务的语义无法用一般的方式检测到。 JProfiler明确支持几种常见的异步和远程请求技术,你可以在请求跟踪设置中启用或禁用它们。默认情况下,不启用请求跟踪。 也可以在会话启动前直接显示的会话启动对话框中配置请求跟踪。
在JProfiler的主窗口中,状态栏显示是否启用了一些异步和远程请求跟踪类型,并为你提供了一个进入配置对话框的快捷方式。
JProfiler会检测是否在分析的JVM中使用了未激活的异步请求跟踪类型,并在状态栏中的异步和远程请求跟踪图标旁边显示一个 通知图标。通过点击通知图标,可以激活检测到的跟踪类型。 异步和远程请求跟踪可能会产生大量开销,只有在需要时才应激活。
异步跟踪
如果至少有一种异步跟踪类型被激活,CPU、分配和探针记录的调用树和热点视图会显示所有激活跟踪类型的信息, 同时还有一个计算"内联异步执行"调用树分析的按钮。在该分析的结果视图中, 所有异步执行的调用树通过 "异步执行"节点与调用点相连。 默认情况下,异步执行测量值不会添加到调用树中的祖先节点。但是有时查看合计值很有用,因此分析顶部有一个复选框允许你在需要的时候做到这一点。
在另一个线程上卸载任务的最简单方法是启动一个新的线程。通过JProfiler, 你可以通过激活"线程启动"请求跟踪类型来跟踪一个线程从创建到执行点的过程。但是,线程是重量级对象,通常会被重复调用, 所以这种请求跟踪类型对于调试更有用。
在其他线程上启动任务的最重要和最通用的方法是使用java.util.concurrent
包中的Executor。
Executor也是许多处理异步执行的更高级别的第三方库的基础。通过支持Executor,JProfiler支持一整套处理多线程和并行编程的库。
除了上面的通用案例,JProfiler还支持两个JVM的GUI工具包。AWT和SWT。这两个工具包都是单线程的,
这意味着有一个特殊的事件调度线程可以操作GUI小部件和执行绘图操作。为了不阻塞GUI,长期运行的任务必须在后台线程上执行。
然而,后台线程经常需要更新GUI以指示进度或完成。这是通过特殊的方法来实现的,
这些方法可以安排一个Runnable
,在事件调度线程上执行。
在GUI编程中,经常要按照多个线程的变化来连接因果关系:用户在事件调度线程上发起一个操作,该线程又通过Executor启动一个后台操作。 完成后,该Executor将推送一个操作给事件调度线程。如果最后一个操作产生了性能问题,那么它与发起事件的线程之间存在两个线程的变换。
最后,JProfiler支持Kotlin协程, 这是为Kotlin后端实现的多线程解决方案。异步执行本身就是一个协程的启动点。Kotlin协程的调度机制很灵活, 实际上可以包含在当前线程上启动,在这种情况下,"异步执行"节点有一个内联部分,然后在节点的文本中单独报告。
挂起方法会中断执行,稍后可能在不同的线程上恢复执行。检测到挂起的方法会有附加 "挂起"图标,并有一个工具提示, 显示实际调用次数与方法的语义调用次数。Kotlin协程可以被故意挂起,但由于它们没有绑定到线程,所以等待时间不会出现在调用树的任何地方。 要查看一个协程执行结束前的总时间,可以在"异步执行"节点下面添加一个 "挂起"时间节点,它可以捕获协程的整个暂停时间。 根据你对异步执行的CPU时间还是挂钟时间感兴趣,在运行中你可以通过分析顶部的"显示挂起时间"复选框添加或删除这些节点。
跟踪非分析调用点
默认情况下,Executor和Kotlin协程跟踪都只跟踪调用点在分析类中的异步执行。 这是因为框架和库可以以与你自己代码执行无直接相关的方式使用这些异步机制,添加这些调用和执行站点只会增加开销和分散注意力。 然而,跟踪非分析调用点是有用处的。例如,一个框架可以启动一个Kotlin协程,然后在其上执行你自己的代码。
如果检测到非分析类中的这类调用点,调用树和热点视图中的跟踪信息会显示相应的通知信息。 在实时会话中,你可以直接从这些视图中分别为Executor和Kotlin协程跟踪开启非分析调用点跟踪。 这些选项可以在会话设置对话框的"CPU分析"步骤中随时更改。
需要了解的是,只有当Kotlin协程的启动发生在CPU记录激活的时候,才能对其进行跟踪。 如果你稍后才开始CPU记录,Kotlin协程的异步执行将不能被内联。JProfiler会像检测非分析类的调用点一样通知你。 如果你需要对在应用程序启动时启动的长生命周期协程进行分析,那么不应该使用Attach模式。 在这种情况下,使用-agentpath VM参数启动JVM,并在启动时开始记录CPU。
远程请求跟踪
对于选定的通信协议,JProfiler能够插入元数据并跟踪跨JVM的请求。支持的技术有:
- HTTP:客户端的HttpURLConnection、java.net.http.HttpClient、Apache Http Client 4.x、Apache Async Http Client 4.x、OkHttp 3.9+, 服务器端的任何Servlet-API实现或没有Servlets的Jetty。
- 对Jersey Async Client 2.x、RestEasy Async Client 3.x、Cxf Async Client 3.1.1+的异步JAX-RS调用的额外支持。
- Web服务:JAX-WS-RI,Apache Axis2和Apache CXF。
- RMI
- gRPC
- 远程EJB调用: JBoss 7.1+和Weblogic 11+。
为了能够在JProfiler中跟踪请求,你必须分析两个VM,并在单独的JProfiler顶层窗口中同时打开它们。 这既适用于实时会话,也适用于快照。如果目标JVM当前未打开,或者在远程调用时CPU记录未激活,点击调用点超链接将显示一条错误信息。
在跟踪远程请求时,JProfiler会在相关JVM的调用树中明确显示调用点和执行点。 JProfiler中的调用点是在执行记录的远程请求被执行之前的最后一个分析方法调用。它启动的任务的执行点位于另一个不同JVM中。 JProfiler允许你通过使用在调用树视图中显示的超链接在调用点和执行点之间进行跳转。
调用点对于所有线程的远程请求跟踪具有相同的标识ID。这意味着当你从调用点跳转到执行点,反之亦然,没有线程解析, 跳转总是激活"所有线程组"以及"所有线程状态"的线程状态选择,这样就保证了目标是显示树的一部分。
调用点和执行点是1:n的关系。一个调用点可以在多个执行点上启动远程任务,尤其是在不同的远程VM中。在同一个VM中, 一个调用点对应多个执行点不太常见,因为它们必须发生在不同的调用堆栈上。 如果一个调用点调用多个执行点, 你可以在对话框中选择其中一个。
执行点是调用树中的一个合成节点,它包含了由一个特定调用点启动的所有执行。执行点节点中的超链接会将你带回该调用点。
如果同一个调用点重复调用同一个执行点,执行点将显示其所有调用的合并调用树。如果不希望这样, 可以使用异常方法功能进一步拆分调用树,如下截图所示。
与只能从一个调用点引用的执行点不同,调用点本身可以链接到多个执行点。通过调用点的数字ID, 如果你看到从不同的执行点引用了同一个调用点,你可以识别它。此外,调用点还显示远程VM的ID。 可以在状态栏中看到被分析的VM的ID。它不是JProfiler内部管理的唯一ID,而是一个显示ID,它从1开始, 并为JProfiler中打开的每一个新分析VM递增。