内存泄漏的代价是巨大的,并且通常直接与生产停机时间或部署进度表延迟有关。 不幸的是,适当的测试解决方案的成本也很高,而且客户通常不愿意-或无法-投资必要的资源。
需要明确的是,解决内存泄漏的最佳方法是在测试中检测并解决它们。 理想情况下,应该有一个测试计划,一个与生产环境相同的测试环境,该环境可以驱动代表性的和异常的工作负载,以及具有专门用于系统测试的技能的技术资源。 这是确保尽可能向生产过渡的最佳方法。 但是,设计和提供这种环境以及相关的文化变化并不是本文的重点。
Java有四种常见的内存使用问题:
Java堆内存泄漏 堆碎片 内存资源不足 本机内存泄漏。WebSphere Application Server引入了两种补充技术来帮助客户解决Java堆内存泄漏(在一定程度上,这些工具还解决了由内存资源不足引起的问题;本文未解决这两个问题的相似性)。
通常,当应用程序无意(由于程序逻辑错误)保留对不再需要的对象的引用时,将导致Java堆内存泄漏。 这些无意的对象引用可防止内置的Java垃圾回收机制释放这些对象使用的内存。 这些内存泄漏的常见原因是:
插入而不删除到集合中 无限缓存 未调用的侦听器方法 无限循环 会话对象过多 编写得不好的自定义数据结构。由于配置问题或系统容量问题,可能导致由于内存资源不足而导致的内存使用问题。 例如,可以使用-Xmx参数将Java虚拟机允许的最大堆大小配置为一个太低的值,以致无法容纳内存中的用户会话总数。 或者,系统的物理内存可能太少,无法容纳当前的工作负载。 本机内存泄漏是非Java代码中的内存泄漏。 例如,在Type-II JDBC驱动程序中,或者在Java进程地址空间的非堆段中出现碎片。
这是一篇介绍性文章,专为Java开发人员,系统管理员和问题确定顾问使用在IBM WebSphere Application Server上部署的应用程序而设计。
有许多问题会导致内存泄漏,这对于系统管理员来说尤其麻烦:
传统的Java内存泄漏根本原因检测技术具有很大的性能负担,可能不适合在生产环境中使用。 这些技术通常包括堆转储分析,附加JVMPI(Java虚拟机分析器接口)或JVMTI(Java虚拟机工具接口)代理,或使用字节码插入来跟踪集合中的插入和删除。
传统的冗余方法(例如群集)只能在一定程度上提供帮助。 内存泄漏将在整个群集成员中传播。 受影响的应用服务器的响应速度会减慢工作负载管理技术。 这可能导致将请求路由到更健康的服务器,并导致协调的应用程序服务器崩溃。
典型的分析解决方案应尝试将应用程序移动到隔离的测试环境中,在该环境中可以重新创建问题,并且可以执行分析而不会影响生产服务器。 在这些测试环境中,复制内存的困难使关联的内存泄漏的成本增加了。
这些问题的原因是传统技术试图同时进行检测和分析。
WebSphere Application Server V6.0.2和更高版本提供了一个分为两个阶段的解决方案,该解决方案将检测问题与分析分开。
第一步是在生产WebSphere Application Server运行时中运行的轻量级内存泄漏检测机制。 这种轻量级的检测技术使用便宜的,通用的Java堆使用情况统计信息来监视内存使用情况趋势,并提早通知内存泄漏。 这使管理员有时间准备适当的备份解决方案,并以脱机方式分析问题的根本原因,而不会产生与测试环境中的复制相关的昂贵且令人沮丧的问题。
解决方案的第二阶段是脱机工具:Java内存转储诊断(MDD4J),用于分析生产应用程序服务器外部的堆转储。 这是一个重量级的脱机内存泄漏分析工具,将多个现有的堆转储分析工具合并到一个用户界面中。
为了缩小检测与分析之间的鸿沟,已为在IBM JDK上运行的WebSphere Application Server提供了自动堆转储生成工具。 在检测到内存泄漏模式后,此工具将生成多个堆转储,这些堆转储已与足够的内存泄漏进行了协调,以便于使用MDD4J进行比较分析。 另外,如果检测到OutOfMemoryError,则将IBM JDK配置为自动生成堆转储。 管理员应设置负载平衡,或在使用率较低时生成堆转储,以避免短期内性能下降。
轻量级内存泄漏检测是通过监视空闲内存的下降趋势来实现的。
泄漏可能非常快,也可能非常缓慢,因此可以分析短间隔和长间隔的内存使用趋势。 另外,分析了垃圾回收周期后内存使用率的下降趋势,以检测垃圾回收周期后平均可用内存低于某些阈值的情况。 这种情况要么是内存泄漏的迹象,要么是在资源太少的应用程序服务器上运行应用程序。 对于所有平台,此轻量级内存泄漏检测工具可从版本6.0开始在所有版本的WebSphere Application Server上使用。
另外,专门针对iSeries®平台,iSeries上的WebSphere Application Server包括其他功能,用于检测Java堆大小是否将扩展到DASD上,并发出警报通知管理员即将发生这种情况,以及是否存在这种情况。或者是:
有效内存池大小太小 资源太少 内存泄漏。zSeries®支持在6.0.2版中仅包含单个服务方拓扑,但在6.1版中进行了扩展以包括多个服务方拓扑。 单服务方拓扑将V6.0.2中的内存泄漏检测范围限制为问题确定或测试环境。
图1显示了由内存泄漏检测功能生成的示例通知。 该通知是通过JMX发送的,显示在管理控制台中,并记录在服务器日志中。
自动堆转储生成工具(仅在IBM JDK上可用)在明显的内存泄漏迹象之后但在应用服务器由于OutOfMemoryError崩溃而生成堆转储。 足够的内存泄漏后,此功能会生成第二个堆转储。 这两个堆转储有助于使用MDD4J进行比较分析。
自动堆转储生成可以默认启用,也可以在适当的时候使用MBean操作启动。
尽管可以使用轻量级的内存泄漏检测,并且可以在WebSphere Application Server和WebSphere Application Server Network Deployment中立即使用,但它还是可以完全配置的,并且可以与高级自主管理器或定制JMX客户端进行交互。 WebSphere Extended Deployment是这种关系的一个示例。
WebSphere Extended Deployment提供了许多配置内存泄漏检测的策略。 一种策略可能是通过对多个堆转储(使用工作负载管理来维护应用程序的性能)进行分析来对内存泄漏通知做出React。 另一个策略可能只是监视应用程序服务器的内存级别何时至关重要,以便在损坏之前重新启动应用程序服务器。
一旦检测到内存泄漏并生成了堆转储,就可以将其转移到生产服务器之外,并进入问题确定机器进行分析。
Java内存转储诊断(MDD4J)是一种脱机堆转储分析工具,可帮助确定内存泄漏的根本原因。 分析机制识别出怀疑的泄漏数据结构类和包。 通过此标识,系统管理员可以将内存泄漏的根本原因缩小到几个组件应用程序。
WebSphere Application Server提供了用于托管第三方J2EE应用程序的容器。 随着Java堆栈的发展,随着时间的流逝,越来越多的抽象和组件化层被添加到基本Java应用程序和WebSphere Application Server堆栈中。 当发生内存泄漏时,这给确定问题提出了巨大的挑战。 对于遇到内存泄漏的系统管理员来说,托管许多第三方应用程序的WebSphere Application Server就像一个黑匣子。 在这种情况下,第一步是将内存泄漏的根本原因缩小到一个或几个组件。 根本原因通常在于一个错误的组件应用程序。 借助Java内存转储诊断工具的分析结果,系统管理员现在可以更快地识别出故障组件,而无需IBM的任何支持。
一旦确定了故障组件,系统管理员就可以联系开发人员,他们可以在较小的环境中复制该问题,并在日志记录中使用调试器或特定的跟踪语句来识别故障的源代码方法,并进行必要的更改应用程序代码或配置来解决内存泄漏问题。 有时,了解故障组件或泄漏对象足以使我们确定一些常见的配置问题。 例如,找到许多HTTP会话对象将导致我们查看HTTP会话超时配置。
该工具提供两种主要类型的分析功能:
单个转储分析最常与内存转储一起使用,内存转储是由Java技术版的IBM Developer Kit(具有OutOfMemoryExceptions异常)自动触发的。 这种类型的分析使用启发式过程来识别具有包含大量子对象的容器对象的可疑数据结构。 这种启发式方法对于检测泄漏的Java集合对象非常有效,这些对象在内部使用数组存储所包含的对象。 已经发现,这种启发式方法在IBM支持部门处理的大量内存泄漏案例中是有效的。
比较分析用于比较在内存泄漏应用程序的单次运行(即,空闲Java堆内存正在删除)期间执行的两个内存转储(主转储和基准转储)。 比较分析非常适合与轻量级内存泄漏检测结合使用。 为了进行比较分析,主转储是指在内存泄漏显着发展之后(消耗大量的最大已配置堆大小)进行的转储。 基线转储指的是由于内存泄漏尚未显着消耗堆的早期进行的堆转储。 转储之间的堆消耗越大,分析结果越好。
比较分析技术可以识别出一组大型数据结构,这些结构在组成数据类型的实例数量上显着增长。 在每个转储中将数据结构分组在一起,然后在主要转储和基准转储中进行匹配和比较,以识别可疑的数据结构正在经历大量增长。 该技术与当今市场上许多分析工具中可用的基本堆转储差异技术不同,因为该技术以较高的粒度级别识别可疑泄漏数据结构,而不是识别可疑泄漏数据类型(例如,字符串的粒度要低得多)。 例如,MDD4J会告诉您某个特定的容器(例如特定的EJB对象)正在泄漏大量的字符串,而不是简单地告诉您从某个未知源泄漏了大量的字符串。 识别可疑数据结构有助于更好地了解内存泄漏的根本原因。 在图3的树视图中描述了一个示例数据结构,在其中可以看到不仅字符串对象正在泄漏,而且还从类中的HashSet以MyClass的名称引用了它们。
分析结果显示在具有以下功能的基于Web的交互式用户界面中:
列出分析结果,堆内容,大小和增长的摘要。
列出可疑的数据结构,数据类型和程序包,这些数据结构有助于进行比较分析的堆使用量的增长,并有助于进行单个转储分析的堆的大容量。
所有权上下文视图显示了主要足迹贡献者汇总集合中占足迹的主要贡献者与重要构成数据类型之间的所有权关系。
交互式树视图中的浏览功能显示堆转储的相关部分,显示堆中所有可疑容器对象的所有传入(仅在树中显示一个引用,其余分别显示)和传出引用以及容器的子对象对象,根据到达范围排序。
从可疑列表到所有权上下文,以及从内容视图到浏览视图的导航功能。
具有过滤器和排序列的内存转储中所有对象和数据类型的列表视图。
MDD4J工具结合了许多现有工具的最佳功能。 比较分析技术是基于Leakbot研究项目(参见相关主题 )。 单转储分析功能使用它也可以在HeapAnalyzer工具下载从alphaWorks(参见发现分析技术相关主题 )。 该表格的观点是基于从一个命令行工具的功能,HeapRoots(参见相关主题 )。
使用WebSphere Application Server V6.1,用于Java的内存转储诊断(版本1.0)工具与IBM Support Assistant工具(版本3.0)打包在一起,IBM Support Assistant工具(系统管理员的独立工具)也可以从以下位置单独安装(请参阅参考资料 )。可以将堆转储转移到脱机分析的任何机器上的WebSphere Application Server。 (请参阅相关主题 ,了解如何从IBM Support Assistant的3.0版推出MDD4J 1.0版的信息。)
下载IBM Support Assistant之后,可以使用Updater机制分别添加Java的Memory Dump Diagnostic:在IBM Support Assistant中,在Updater视图中选择New Products and Tools => Common Component and Tools (还请确保WebSphere还安装了Application Server V6.0或V5.0产品插件)。
此新版本的MDD4J(版本1.0)具有许多与可伸缩性相关的改进,并且比以前的版本(版本0.97a)支持64位堆转储。 (可以将MDD4J的先前版本下载为developerWorks的技术预览;请参阅参考资料 。)
以下示例显示了一个简单的内存泄漏Java应用程序的内存泄漏分析结果。 此示例应用程序将字符串对象泄漏到静态HashMap中。 轻量级泄漏检测能够在早期识别泄漏的存在并自动生成适当的堆转储。 然后使用Memory Dump Diagnostic for Java Version 1.0中的比较分析对这些转储进行脱机分析。 图2,图3和图4显示了显示可疑内存泄漏的分析结果。
借助WebSphere Application Server V6.1,系统管理员可以在生产环境中及早接收到有关内存泄漏的通知,而无需使用字节码插入或任何附加的代理。 轻量级内存泄漏检测旨在将对性能的影响降到最低,并提供在适当的时间自动生成堆转储的功能,以确保准确的分析结果。
可以使用Java Memory Dump Diagnostic(MDD4J)离线分析手动生成的堆转储,或借助内存泄漏检测进行堆转储。 该工具可以帮助系统管理员将问题分配给适当的组件,以便开发人员可以在其计算机上轻松复制分析结果。 MDD4J为开发人员和问题确定顾问提供了一种工具,用于识别泄漏的候选对象,浏览堆和所有权链,以确定其代码中泄漏的数据结构。
这项技术极大地有助于诊断Java堆内存泄漏检测和分析,并且可以与良好的系统测试过程或生产系统一起使用。
作者要感谢:Daniel Julin和Stan Cox审阅了这篇论文; LeakBot项目的首席研究员Nick Mitchell的领导能力和创新能力; Mark T Schleusner在开发MDD4J方面的协助与合作。
翻译自: https://www.ibm.com/developerworks/websphere/library/techarticles/0606_poddar/0606_poddar.html
相关资源:25个经典网站源代码