IBM 的 Java 诊断, 使用面向 Java 的 Lock Analyzer 诊断同步和锁问题

JavaGG 发布于 2009/07/15 11:35
阅读 580
收藏 1
面向 Java™ 的 IBM® 锁分析器可从 alphaWorks 获得,可对运行中的 Java 应用程序进行实时锁监视。锁争用会降低应用程序性能,该工具会突出显示发生锁争用的线程。 开发人员可以使用该信息修改其应用程序以减少锁争用,从而提高性能。 本文介绍了面向 Java 的 IBM 锁分析器,介绍了其构建的基础架构并针对该工具的未来发展方向进行了思考。
< include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters >
关于本系列

IBM 的 Java 诊断 介绍了来自 IBM 的新工具,可帮助解决 Java 应用程序中的问题并提高性能。您可从每篇文章学到新的知识,并且马上就可以投入使用。

为本系列投稿的每位作者都属于一个新团队,它致力于创建新工具帮助您解决 Java 应用程序的问题。每位作者都有着不同的知识背景,并为团队带来各种技能和专业领域的知识。

您可以单独联系各个作者,讨论您对这些文章的评论或问题。

当今很多 Java 应用程序都通过使用线程来利用该语言的功能,从而支持并发编程。很多线程可以共享相同的数据对象,但是如果应用程序中控制线程的部分设计得不好,则您可能会遇到锁争用问题从而降低性能。

例如,在多线程应用程序中,如果多个线程访问相同的资源进行读写访问, 则可能会出现线程同步问题。如果一个线程尝试读取某个文件,而另一个线程 对其进行写访问,则可能会损坏数据。为了解决这个问题,Java 语言允许一个线程获得对对象的专有锁, 从而防止其他任何线程访问它。一旦该线程完成对该对象的处理之后,便释放该对象,以便 其他线程可以进行访问。但是,如果应用程序没有进行过精心设计,则这种机制可能会导致更高级别的争用。

如果一个锁正在使用中,而另一个线程尝试获取它,这时会发生锁争用。 如果锁被频繁获得和/或持有时间较长,则可能会发生更高级别的争用。 更高级别的锁争用(很多线程尝试访问它)将成为系统中的瓶颈, 因为每个运行的线程将暂停执行,直到它所需的锁变为可用为止, 这样便限制了应用程序的性能。

使用 JLA 防止出现锁问题

面向 Java 的 IBM 锁分析器 (JLA) 是 alphaWorks 中的新工具,它可以 在运行 IBM 提供的 Java SDK 或 JRE 版本 5.0 或更高版本的的任何平台上运行, 对活动应用程序执行锁分析,以突出显示线程的活动并且提供对锁争用频率的深入洞察。该视图可以帮助解决锁争用问题以及性能问题。

JLA 一边运行现有的 Java 应用程序,一边收集该应用程序的所有锁信息。记录每个锁及其详细信息,如保持的时间、是否争用以及它阻塞线程尝试获得它的时间百分比。该工具由运行时库和前端图形用户界面组成,前者必须在启动所要监视的应用程序时加载,后者可以显示结果和性能分析。

 




回页首



JLA 设计详细信息

JLA 由以下两个程序包组成:

  • JLAagent。该程序包与平台有关,由特定于平台的库文件和一组打包成 JAR 的 Java 类组成,它提供与 Java 虚拟机 (JVM) 的连接以收集有关运行的应用程序的锁统计信息。

  • JLAGui。该程序包是使用 Swing 编写的,它与平台无关,提供图形用户界面。

JLAagent 中的本地库是用 C 编写的并且使用 Java 虚拟机工具界面 ( Java Virtual Machine Tool Interface,JVM TI)。该界面提供方法控制在 JVM 中运行的应用程序的执行。通过该界面,您可以利用 IBM 发布的 JVM 中可用的功能收集线程统计信息。还可以通过对您要分析的 Java 应用程序的启动命令进行修改,让 JVM 在启动时加载此本地库。然后本地库使用与它一起打包的 Java 类创建一个平台 MBean 服务器,该服务器提供允许 JLAGui 连接 JLAagent 的机制。

启动 MBean 服务器之后, JLAGui 可以通过将该服务器导航到本地库中,然后导航到运行的 JVM 中,从而请求运行的应用程序的锁统计信息。由于 JLAagent 和 JLAGui 通过 MBeans 连接,因此它们没有必要在同一机器上运行;JLAGui 还可以从网络上的任意位置连接到 MBean 服务器。

 




回页首



运行 JLA

若要运行 JLA,您必须首先为您的应用程序配置 JLAagent。 然后您才可以启动 JLAGui 开始监视锁性能。如果您尚未这样做, 请 下载 JLA 以便您可以跟随本文的操作。

配置应用程序以使用 JLA

有很多版本的 JLAagent,IBM 提供的 JRE 支持的每个平台使用一个版本;您必须确保使用的 JLAagent 与您要监视的应用程序将运行的平台相对应。如果您使用了错误版本的 JLAagent,则尝试监视的应用程序将无法加载并且将崩溃。根据将要启动它的 JRE 的基础架构,每个 JLAagent 程序包都具有一个名称。

在运行所要监视的应用程序的计算机上,将 JLAagent 程序包解压缩到某个目录。将该目录添加到 Java 类路径和系统路径。属性文件名为 JLAtiagent。属性包含在该程序包中;它包含有关启动 MBean 服务器的连接信息。如果您想修改默认值,可以编辑该属性。

若要为将进行分析的应用程序加载 JLAagent,请修改该应用程序的启动命令。 添加 -agentlib:JLAtiagent 参数以便它加载本地库, 将添加的 JLAtiagent.jar 添加到类路径, 并指定 JLAtiagent.properties 文件的位置。

例如,启动命令 java dummy.class 将需要进行如下修改:

java -Dcom.sun.management.config.file=JLAtiagent.properties 
     -agentlib:JLAtiagent -cp .;JLAagent.jar dummy.class

如果此时出现异常,请进行检查以确保使用正确的 JLAagent 版本。如果一切正常,将会在命令行上显示消息 JLA 客户机正在注册 MBeanServer

此时,JLAagent 已经指导 VM 开始记录锁详细信息,即使 JLAGui 未运行也是如此。这意味着当启动 JLAGui 时,您将能够查看整个应用程序生命周期中的锁统计信息。

JLAGui

您可以使用以下命令启动 JLAGui:

java -jar JLAGui.jar

 

默认情况下, JLAGui 将查找在本地主机端口 1972 上运行的 MBean 服务器;同样不需要指定这些值。但是如果您想使用不同的值,则可以在命令行上修改主机或端口值,如表 1 所示:


表 1. JLAGui 的连接选项

设置默认值说明
-Dcom.ibm.jla.port 1972 需要匹配 JLAtiagent 使用的 com.sun.management.jmxremote.port
-Dcom.ibm.jla.hostname 本地主机 如果想监视在远程机器上运行的应用程序,可以设置为 IP 地址或有效的主机名

当启动 GUI 时,它将尝试通过机器上运行的 MBean 服务器以及在启动选项中指定的端口号连接到代理程序。 如果连接成功,则图 1 中的屏幕将显示:


图 1. 初始 JLA 屏幕
初始 JLA 屏幕

如果连接未成功,则会显示错误消息,并提供 GUI 尝试连接的详细信息。仔细查看错误消息以确保连接正确。如有必要,您可以关闭 GUI,然后用修改后的命令行选项重新启动。您还可以进行检查以确保要分析的应用程序正在运行附带的本机代理程序。

单击 Create Report 按钮即可创建正在监视的应用程序当前线程统计信息的快照报告。您将看到三个面板(参见图 2),它们显示 Java 监视程序(应用程序创建的那些监视程序)、系统监视程序(VM 创建的那些监视程序)以及摘要报告页。应用程序创建或使用的任何监视程序在表和图中都有一个条目。每个监视程序只有一个条目可见,即使有多个线程访问它也是如此。将在监视程序级别而不是线程级别记录该数据。


图 2. JLA 报告示例

每一列的高度基于慢锁计数的值,它与图中的所有列有关。当请求的监视程序已经被另一个线程拥有并且请求线程被阻塞时会发生缓慢计数。每个条状图形的颜色基于 %MISS 列的值(参见表 2),从红色 (100%) 逐渐过渡到黄色 (50%),并且最终变为绿色 (0%)。红色条表示每次请求监视程序时线程都会阻塞,而绿色条表示线程从不阻塞。对图进行快速扫描将显示执行得不太好的那些监视程序。

表 2 解释报告表中各列值的说明:


表 2. 报告表图例

列名说明
%MISS 当请求的线程被阻塞以等待监视程序时总 gets(获取)的百分比。
GETS 成功获取的总数。
NONREC 非递归获取的总数。该数包括 SLOW gets。
SLOW 非递归获取(导致阻塞请求线程以等待释放监视程序)的总数。NONREC 中包含该数。
REC 递归获取的总数。递归获取表示请求的线程已经拥有监视程序 。
TIER2 支持三层自旋锁的平台上,层 2(内部自旋循环)迭代的总数。
TIER3 支持三层自旋锁的平台上,层 3(外部线程让步循环)迭代的总数。
%UTIL 监视程序持有时间除以时间间隔。必须打开持有时间计数。
AVER_HTM 监视程序的平均持有时间;不包括递归获取,因为递归获取时已经拥有监视程序。
MONITOR NAME 监视程序名称,如果不知道名称,则为 NULL(空白)。

 




回页首



运行中的 JLA

一个简单的应用程序示例将帮助您了解如何使用 JLA 帮助查找锁争用的区域。假设一个应用程序由两个线程组成,而这两个线程都尝试访问同一个对象。本例中,该对象就是一个名为 JLAsink 的类,该类包含两个同步的方法,一个用于设置数据块,一个用于检索该数据块。这两个线程同时开始并且都在一个循环中运行以同时访问 JLAsink。一个线程称为 setter 方法,另一个线程称为 retrieval 方法。

图 3 显示在性能较差的示例应用程序中运行 JLA 的结果:


图 3. 最初的 JLA 快照
在性能较差的应用程序中运行 JLA 的快照

可以从此屏幕截图中看到,JLAsink 监视程序进行了 9,161 次 get,并且请求线程被阻塞,阻止它获得 25% 的锁时间。很明显,当处理尝试获得锁的线程时,应用程序的这部分性能将有所下降。

图 4 显示在更高效的示例应用程序中运行的 JLA:


图 4. 在优化的应用程序中运行 JLA 的快照
线程更改后的结果

您可以看出 JLAsink 监视程序进行了 9,370 次 get,并且没有阻塞任何一个线程。这表明在处理锁争用时,该监视程序运行得比较理想。现在优化的代码比未优化的代码运行速度更快,您可以通过比较每次运行的时间间隔看到这一点。 第二次运行访问 JLAsink 对象的次数比第一次运行多,并且它花的时间较少。

 




回页首



未来计划

随着当前功能的完善,该工具的发展正逐渐稳定。将来,我们的团队将在 JLA 的基础上发布一个新工具,该工具将超越锁统计信息,提供对活动 Java 应用程序的分析。该工具不仅仅具有 JLA 的功能,而且还具有 EVTK 和 Dump Analyzer 工具(在 本系列的前面部分文章 中您已经学习了这些工具)的功能。目前该工具还处于设计阶段。

加载中
返回顶部
顶部