WebSphere Application Server 中的内存泄漏检测与分析: 第 2 部分:用于泄漏检测与分析的工具和功能

本文深入探讨了WebSphere Application Server中内存泄漏的检测与分析,介绍了新引入的内存泄漏检测功能和MDD4J工具。通过区分Java堆内存泄漏和本机内存泄漏,解释了内存泄漏的常见原因,如无限缓存、未调用的侦听器方法和无限循环。WebSphere提供了轻量级检测机制,用于生产环境,并提供了MDD4J工具进行离线分析,帮助确定内存泄漏的根源。文章通过多个案例研究展示了如何使用这些工具进行分析,强调了它们在减少内存泄漏带来的成本和影响方面的优势。
摘要由CSDN通过智能技术生成
 
本文介绍 Java™ 应用程序中关于内存泄漏的初步知识,并包含有关为解决 IBM® WebSphere® Application Server 中的这些问题而设计的工具的动机、范围和用法信息。

引言

企业应用程序中的内存泄漏会导致大量的危急情况。付出的代价包括用于进行分析的时间和资金、生产环境中开销巨大的停机时间、压力以及对应用程序和框架丧失信心。

非代表性测试环境、无效的工作负载标识和不充分的测试周期都可能导致在测试过程中不能检测出内存泄漏。公司通常无法或者不愿意投入大量的必要时间和资金来克服这些缺陷。导致这种情况的原因在于教育、文化和财务中的某个方面出现了问题。本文并不尝试解决上述问题,而是侧重介绍帮助解决由这些原因造成的结果的技术解决方案。

本文是入门文章第 1 部分:内存泄漏概述的后续部分。在第 2 部分中,我们将更详细地介绍 WebSphere Application Server V6.1 中的内存泄漏分析和检测功能以及一些实际的案例研究。本文将介绍 WebSphere Application ServerV6.1 中新引入的内存泄漏检测功能和名为 Memory Dump Diagnostic for Java (MDD4J) 的脱机内存泄漏分析工具。结合使用这两项功能可以确定运行于 WebSphere Application Server上的 Java 和 Java 2 Platform Enterprise Edition (J2EE™) 应用程序中内存泄漏的根源。

本文主要面向 Java 开发人员、应用服务器管理员和处理部署在 WebSphere Application Server 上的应用程序的问题确定顾问。





回页首


什么是 Java 中的内存泄漏?

在 Java 应用程序中,当对象保留对不再需要的对象的引用时将会发生内存泄漏。尽管 Java 虚拟机 (JVM) 具有内置的垃圾收集机制(请参见参考资料),无需程序员负责任何明确的对象重新分配责任,但是此问题仍会阻止自动 Java 垃圾收集进程释放内存。这些内存泄漏问题表现为,随着时间的推移会增加 Java 堆使用量,并在堆完全耗尽时最终导致 OutOfMemoryError 错误。这种类型的内存泄漏称为 Java 堆内存泄漏。





回页首


碎片和本机内存泄漏

在本机系统资源(如文件句柄、数据库连接构件等)不再使用后,如果未能清理它们,也会造成 Java 中的内存泄漏。这种类型的内存泄漏称为本机内存泄漏。这些类型的内存泄漏表现为,随着时间的推移会增加进程大小,而在 Java 堆使用量方面没有任何增加。

尽管 Java 堆内存泄漏和本机内存泄漏最终都表现为 OutOfMemoryError 错误,但是并不是所有的 OutOfMemoryError 错误都是由 Java 堆泄漏或本机内存泄漏导致的。在压缩过程中,如果 Java 垃圾收集进程无法释放任何连续的可用内存块来存储新对象,则碎片 Java 堆也可能导致 OutOfMemoryError 错误。在这种情况下,不论是否有大量的可用 Java 堆,都可能发生 OutOfMemoryError 错误。在 IBM 的 SDK 1.4.2 版或更低版本中可能发生碎片问题,原因是在 Java 堆中存在 Pinned 对象或 Dosed 对象。Pinned 对象是在堆压缩过程中由于 JNI(Java 本机接口)对其进行访问而无法移动的那些对象。Dosed 对象是在堆压缩过程中由于线程堆栈的引用而无法移动的对象。常常由于频繁分配较大的对象(超过 1 MB)而加剧了碎片问题。

因碎片问题或本机内存泄漏而导致的 OutOfMemoryError 错误不在本文讨论之列。通过观察 Java 堆随时间推移的使用情况可以将本机内存泄漏和碎片问题与 Java 堆内存泄漏区别开来。可以使用 IBM Tivoli® Performance Viewer 和详细的垃圾收集输出(请参见参考资料)进行区分。不断增加的 Java 堆的使用量可导致堆全部耗尽,这指示存在 Java 堆内存泄漏,而对于本机内存泄漏和碎片问题,堆的使用量不随时间的推移表现出明显的增加。对于本机内存泄漏,进程大小将增加,对于碎片问题,在发生 OutOfMemoryError 错误时,会存在大量的可用堆。





回页首


在 Java 应用程序中导致内存泄漏的常见原因

如上文所述,Java 中内存泄漏(Java 堆内存泄漏)的最常见原因是意外的(由于程序逻辑错误)对象引用,从而在 Java 堆中保留未使用的对象。在这一部分中,将介绍导致 Java 堆内存泄漏的许多常见的程序逻辑错误类型。

无限缓存

内存泄漏的一个非常简单的例子是充当缓存而又无限增长的 java.util.Collection 对象(例如,HashMap)。清单 1 显示了一个简单的 Java 程序,该程序演示了基本内存泄漏数据结构。


清单 1. 将字符串对象泄漏到静态 HashSet 容器对象的 Java 程序示例
				
public class MyClass {
  staticHashSet myContainer = new HashSet();
  HashSet myContainer = new HashSet();
  public void leak(int numObjects) {
    for (int i = 0; i < numObjects; ++i) {
      String leakingUnit = new String("this is leaking object: " + i);
      myContainer.add(leakingUnit);
    }
  }
  public static void main(String[] args) throws Exception {
    {
      MyClass myObj = new MyClass();
      myObj.leak(100000); // One hundred thousand
    } 
    System.gc();
  }
			

在清单 1 所示的 Java 程序中,有一个名为 MyClass 的类,它含有对名为 myContainer 的 HashSet 的静态引用。在 MyClass 类的 Main 方法中,存在一个子范围(以加粗文本显示),其中实例化了 MyClass 类的实例,并调用了其成员操作 leak。这会导致将十万个字符串对象添加到容器 myContainer 中。程序控制退出子范围后,MyClass 对象的实例作为垃圾被收集,因为在子范围的外面没有对 MyClass 对象实例的任何引用。不过,MyClass 类对象具有对称为 myContainer 的成员变量的静态引用。由于此静态引用,即使 MyClass 对象的单一实例以及 HashSet 被作为垃圾收集之后,myContainer HashSet 仍会继续永久保留在 Java 堆中,HashSet 中所有的字符串对象会继续永久保留,并占用 Java 堆的大部分容量,直到程序退出 Main 方法为止。此程序演示的基本内存泄漏操作涉及缓存对象中的无限增长。使用 Singleton 模式实现的大多数缓存都涉及本例所示的对顶级 Cache 类的静态引用。

未调用的侦听器方法

许多内存泄漏是由于程序错误产生的,此类错误导致清除了未调用的方法。在 Java 程序中,侦听器模式是常用的模式,使用此模式可以实现用于清除不再需要的共享资源的方法。例如,J2EE 程序通常依靠 HttpSessionListener 接口及其 sessionDestroyed 回调方法,来清除用户会话到期时在其中存储的任何状态。有时,由于程序逻辑错误,负责调用侦听器的程序可能无法调用它,或者侦听器方法可能因出现异常而无法完成调用,这都可能导致未使用的程序状态即使不再需要也仍保留在 Java 堆中。

无限循环

有些内存泄漏是由于程序错误发生的,在存在此类错误的情况下,应用程序代码中的无限循环会分配新的对象,并将其添加到可从程序循环范围外面访问的数据结构中。这种类型的无限循环有时是由于多线程访问共享的不同步的数据结构造成的。这些类型的内存泄漏表现为内存泄漏快速增长,其中,如果详细的垃圾收集数据显示在很短的时间内可用堆空间急剧减少,则会导致 OutOfMemoryError 错误。对这种类型的内存泄漏情况,分析短时间内获取的堆转储 (heap dump) 非常重要,这样可以观察正在快速减少的可用内存。在标题为案例研究 3案例研究 4 的部分中,讨论了对 IBM 支持中涉及无限循环的两种不同内存泄漏情况的分析结果。

虽然通过分析堆转储能够标识内存泄漏数据结构,但是标识无限循环中的内存泄漏代码并不简单。在观察到可用内存快速减少的过程中,通过查找获取的线程转储中的所有线程的线程堆栈,可以标识陷入无限循环的方法。IBM SDK 实现会生成 Java 核心文件以及堆转储。此文件包含所有活动线程的线程堆栈,并能够用于标识可能陷入无限循环的方法和线程。

过多的会话对象

许多 OutOfMemoryError 错误是由于没有为支持最大用户负载而配置适当的最大堆大小导致的。一个简单示例是 J2EE 应用程序,它使用内存中的 HttpSession 对象存储用户会话信息。如果不对内存中可以保持的会话对象的最大数量设置最大限制,那么在用户加载高峰期,可能有许多会话对象。这可导致 OutOfMemoryError 错误,它实际上不是内存泄漏,而

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值