线程分析


不正确地使用线程会产生许多不同类型的问题。太多的活跃线程会导致线程饥饿,线程之间会相互阻塞,影响应用程序的活跃度, 或者以错误的顺序获取锁会导致死锁。此外,对于调试来说有关线程的信息也很重要。

在JProfiler中,线程分析分为两个视图部分,"线程"部分处理线程生命周期和捕捉线程转储。 "Monitors & 锁"部分提供了分析多个线程交互的功能。

检查线程

线程历史视图将每个线程在时间线中显示为彩色行,颜色表示记录的线程状态。线程按其创建时间排序,并可按名称进行过滤。 当有Monitor事件被记录后,你可以将鼠标悬停在线程处于"等待"或"阻塞"状态的部分上,并查看相关的堆栈跟踪, 并有一个链接可以进入Monitor历史视图。

在线程Monitor视图中可以看到所有线程的表格视图。如果在创建线程时CPU记录处于活动状态, JProfiler会保存创建线程的名称并将其显示在表中。在底部,显示创建线程的堆栈跟踪。 出于性能方面的考虑,不会向JVM请求实际的堆栈跟踪,而是使用CPU记录的当前信息。 这意味着,堆栈跟踪将只显示那些满足调用树收集的过滤设置的类。

如果在分析设置中启用了占用CPU时间记录,则一个 CPU时间列会被添加到表中。 只有当你记录CPU数据时,才会测量CPU时间。

和大多数调试器一样,JProfiler也可以进行线程转储。线程转储的堆栈跟踪是JVM提供的完整堆栈跟踪,不依赖于CPU记录。 当你选择两个线程转储并点击 显示差异按钮,可以在差异视图中比较不同的线程转储。 也可以从一个线程转储中选择两个线程,然后选择上下文菜单中的显示差异对其比较。

线程转储也可以通过"触发线程转储"触发器操作或通过 API生成。

分析锁状态情况

每个Java对象都有一个相关联的Monitor,它可以用于两种同步操作: 一个线程可以在Monitor上等待,直到另一个线程对其发出通知,或者它可以在Monitor上获得一个锁,可能会阻塞, 直到另一个线程放弃锁的所有权。此外,Java在java.util.concurrent.locks 包中提供了实现更高级锁策略的类。该包中的锁不使用对象的Monitor,而是使用不同的本地实现。

对于上述机制的两种锁状态情况,JProfiler都可以记录。在锁状态情况下,会有一个或多个线程、 一个Monitor或一个java.util.concurrent.locks.Lock 实例,以及一个需要一定时间的等待或阻塞操作。 这些锁状态情况在Monitor历史视图中以表格的方式呈现,在锁状态历史图中以可视化的方式呈现。

锁状态历史图关注的是所有涉及的Monitor和线程的整体关系,而不是孤立的Monitor事件过程。 参与锁状态情况的线程和Monitor被涂成蓝色和灰色的矩形,如果它们是死锁的一部分,则被涂成红色。 黑色箭头表示Monitor的所有权,黄色箭头从等待的线程延伸到相关的Monitor, 而虚线的红色箭头则表示某个线程想要获取Monitor,并且当前正处于阻塞状态。 如果已经记录了CPU数据,则将鼠标悬停在阻塞或等待箭头上时,可以获得堆栈跟踪。 这些工具提示包含的超链接,可将你带到Monitor历史视图中的相应行。

表格式Monitor历史视图显示Monitor事件。它们的持续时间被显示为一列,因此你可以通过对表格进行排序来找到最重要的事件。 对于表格视图中的任何选定行,你可以使用在图表中显示操作跳转到图中。

每个Monitor事件都有一个相关的Monitor。Monitor类列中显示的是Monitor使用的实例的类名, 如果没有与Monitor相关联的Java对象,则显示"[raw monitor]"。在任何情况下,Monitor都有一个唯一的ID, 显示在一个单独的列中,因此你可以将同一Monitor在多个事件中的使用情况关联起来。 每个Monitor事件都有一个执行操作的等待线程,也有一个正在阻塞操作的绑定线程。 如果有的话,它们的堆栈跟踪会显示在视图的下半部分。

如果你对Monitor实例有更多的问题,Monitor历史视图和锁状态历史图中的在堆遍历器显示操作 提供了一个进入堆遍历器的链接,并选择Monitor实例作为一个新对象集。

限制感兴趣的事件

分析Monitor事件的一个基本问题是,应用程序可能会以非常快的速度生成Monitor事件。 这就是为什么JProfiler对等待和阻塞事件有默认的阈值,低于阈值的事件会被立即丢弃。 这些阈值是在视图设置中定义的,并且可以增大,以便关注更长的事件。

对于记录的事件,你可以进一步应用过滤器。Monitor历史视图在视图顶部提供了一个阈值、一个事件类型和一个文本过滤器。 锁状态历史图允许你选择感兴趣的线程或Monitor,并仅显示涉及标记实体的锁状态情况。 感兴趣的事件以不同的颜色显示在时间线上,并且有一个二级导航条来逐步浏览这些事件。 如果当前事件不是感兴趣的事件,你可以看到当前事件和下一个感兴趣的事件之间在任一方向上有多少事件。

除了显示所选线程的锁状态情况或Monitor外,还显示被从图中删除的锁状态情况。 这是因为每个Monitor事件都是由两个这样的锁状态情况定义的,一个是操作开始的情况,一个是操作结束的情况。 这也就意味着,完全空的图是一种有效的锁状态情况,表示JVM中没有锁。

另一个减少需要你关注的事件数量的策略是累积锁状态情况。在锁状态历史图中,底部有一条时间线,显示所有记录的事件。 在其中点击并拖动选择一个时间范围,所有包含事件的数据就会显示在上面的锁状态图中。 在累积图中,每个箭头可以包含多个相同类型的事件。在这种情况下,工具提示窗口会显示事件的数量以及所有包含事件的总时间。 工具提示窗口中的下拉列表显示了时间戳,并允许你在不同事件之间进行切换。

死锁检测

无论Monitor事件是否被记录,当前锁状态图和当前Monitor视图中的数据始终显示。 这些视图显示当前的锁状态情况和正在进行的Monitor事件。锁状态操作通常是短暂的,但在发生死锁的情况下, 这两个视图将显示该问题的永久视图。此外,当前锁状态图会以红色显示产生死锁的线程和Monitor,因此你可以立即发现此类问题。

Monitor使用情况统计

为了从更高的角度检查阻塞和等待操作,Monitor统计视图从Monitor记录数据计算报告。 你可以按Monitor、线程名称或Monitor类对Monitor事件进行分组,并分析每行的累积计数和持续时间。