在本文中,作者通过介绍三个基于实际情况的">案例研究,分享了有关检测和诊断在生产和开源基准应用程序中的性能问题的经验。这些案例研究强调了如何配合使用 Garbage Collection and Memory Visualizer (GCMV) 和 IBM Monitoring and Diagnostic Tools for Java - Memory Analyzer (MAT) 等各种 IBM® 工具,这些工具通过分析详细的垃圾收集日志、堆转储和线程转储,可共同用于诊断复杂的性能问题。
权威支持在每个专栏中讨论 WebSphere® 产品可用的 IBM® Technical Support 的资源、工具和其他元素,以及可以进一步增强您的 IBM 支持体验的技术和新观念。
组合工具和经验
生产级的多层次企业 Java 应用程序的问题确定和性能调优被认为是非常艰巨的任务。幸运的是,有许多诊断工具可以用来检测这类问题的根源。在本文中,我们通过介绍三个基于实际情况的案例研究,分享了有关检测和诊断在生产和开源基准应用程序中的性能问题的经验。这些案例研究涵盖了一系列的性能问题:
由于基础架构代码中的内存泄漏造成不可用性 由于错误的开发代码造成系统 CPU 使用率高 因不正确的运行时策略和调优而加剧的应用程序设计问题,造成 CPU 和内存使用率高。
每个案例都提出问题,以及使用 Garbage Collection and Memory Visualizer (GCMV) 和 IBM Monitoring and Diagnostic Tools for Java - Memory Analyzer (MAT) 等 IBM 工具,通过分析详细的垃圾收集 (GC) 日志和 heapdump 解决问题的方式。这些案例研究强调了与企业 Java 应用程序的问题确定和性能分析有关的复杂性。这里介绍的所有工具都作为 IBM Support Assistant 工作台 中的插件提供。
案例研究 1:基础架构代码中的内存泄漏
我们的第一个案例研究涉及一个开源基准,其名称为 RUBiS。RUBiS 模拟一个拍卖网站。它配备了自己的工作负载驱动程序,并且有一个 Web 层连接到一个数据库层。在有关性能评估的研究论文中已大量引用它。
当使用 RUBiS 作为我们进行其他研究的一个基准时,我们注意到,RUBiS 在实验结束时会停止响应,并且必须重新启动才可以进行下一个实验。我们正在使用 servlet 版本的 RUBiS 和 MySQL JDBC 驱动程序。我们使用 GCMV 对 100 个客户端的恒定负载分析了该应用程序详细的 GC 日志。通过 Java 命令行或通过对大多数商用应用服务器的管理用户界面,可以很容易地启用详细的 GC 日志。结果显示,即使在恒定负载(参见图 1)的情况下,所使用的堆也始终在不断增长。这似乎是一个典型的内存泄漏问题。然而,我们发现,该内存泄漏的根本原因并没有像大部分经典的内存泄漏那么明显。
图 1. GCMV:在恒定负载的情况下,RUBiS 堆的使用量在不断增长
对于内存泄漏分析,最好先了解一些基本定义:
Shallow size,一个对象的 Shallow
大小是指用于存储对象本身所分配的内存量,没有考虑到所引用的对象。 Retained size,一个对象的 Retained 大小是其 Shallow 大小加上只可以从该对象直接或间接访问的对象的 Shallow 大小。 Retained set 为 X,如果 X 是被收集的垃圾,那么 Retained 集所代表的一组对象就将是被收集的垃圾。 Dominator tree 是一个转换,将循环的对象图转换成 Keep-Alive 树,树中的每个节点直接负责保持其子节点的活动状态。
我们首先使用 MAT 在几分钟内在恒定负载条件下提取此应用程序的两个堆转储,并比较它们。在大多数商用应用服务器中都可以很容易地启用堆转储。快速浏览一下在这两个堆转储中的对象计数,可以使我们注意到一组已经比原始数量和大小增长了近两倍的对象。这些对象大多是原始类型,但增长得最多的那些与 JDBC 中的 PreparedStatement(这种对象的数量在第一个和第二个堆转储之间几乎翻了一番)调用相关。图 2 显示了增长最多的对象的快速分解。在大多数内存泄漏中,有一些容器对象的大小增长最多,而另一些对象类型则是计数的增长最多。