前言
从所周知,Java虚拟机(JVM)及垃圾收集器(GC)负责管理大多数的内存任务,但是Java应用系统中还是有可能出现内存泄漏。事实上,OOM之类的现象在大型项目中也是一个常见的问题。避免内存泄漏的第一步是要弄清楚它是如何发生的,然后对症下药。
那究竟是什么导致了 Java 程序中的内存泄漏呢?难道 Java 虚拟机的垃圾收集器不应该管理未使用的内存吗?是的,它会进行管理,但是垃圾收集的对象只能是不再被引用的对象。但是,某些不再需要的对象,却在系统的某个地方仍在引用它,这样就不能对这些对象进行垃圾收集,在日志中的大量String对象的生成以及编写Java代码时的一些常见的内存泄漏陷阱等等都会造成内存泄漏,但是要在开发阶段完成找出造成泄漏的代码是非常困难的。
在大型企业系统中,Java代码中的内存泄漏是常见而且难于解决的问题。这些泄漏问题通常是在最不愿意它发生的正式生产环境中发现的,而且它也很难于在开发与测试环境中得到重现。这是为什么呢?生产环境中的系统需要处理更大量的数据,而且有可能在运行很长时间后才会发现
Java堆在缓慢地增长。最终,导致系统内存耗尽。
因此本文介绍一种新工具BEA JRockit Mission Control,用来诊断泄漏并指出根本原因。该工具的开销非常小,因此可以使用它来寻找生产环境中的系统的内存泄漏。
简介
BEA JRockit Mission Control(以下简称为JRMC)于2005年12月面世,并从JRockit
R26.0.0版本开始捆绑了这个工具套件,目前最新的版本是2.0.1。它是一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。它包括三个独立的应用程序:内存泄漏监测器(Memory
Leak Detector)、JVM运行时分析器(Runtime Analyzer)和管理控制台(Management
Console)。
下面的架构图描述了BEA JRockit Mission Control 2.0工具套件之间的关系。
JRockit Management Console
JRockit Management Console是一个基于JMX的控制台,用于监控和管理多个JRockit实例,提供至关重要的状态数据和控制JRockit
JVM的运行时特性的方法。它捕获并显示关于垃圾收集器(GC)暂停、内存、堆使用和CPU负载的实时数据,以及部署在JVM内部MBean服务器上注册的所有JMX
MBean所公开的信息。JVM管理包括对CPU相似性、垃圾收集策略和内存池大小的动态控制,还包括一个开销低的方法分析器和一个异常计数器。
如下图所示,可以用多个JRockit Management Console实例连接到一个JRockit
JVM上,而且一个Management Console也可以连接到多个JRockit JVM。Management
Console可以运行在不同的机器上。实际上,控制台是可以连接处理多个JRockit JVM的,通常不需要在同一台机器上运行多个Management
Console,而是在不同的监控机器上面运行多个实例进行JRockit JVM。
JRockit Runtime Analyzer
JRockit Runtime Analyzer(JRA)是一个JVM分析器,是一个随需应变的“动态记录器”Java应用程序,它记录了Java应用程序和JVM在一段预定的时间内的详细记录。然后通过JRA应用程序对记录下来的文件进行离线分析。所记录的数据包括对方法的调用跟踪、错误的同步、锁定的分析,还有垃圾收集统计信息,优化决策以及对象统计信息和其他重要的应用程序/JVM行为。它的目的是让JRockit开发人员能够找到良好的方法来基于现实应用程序优化JVM,对于帮助客户在生产和开发环境中解决问题十分有用。
JRA由两个部分组成:JVM中的记录引擎和可以用于分析结果记录的GUI应用程序。记录引擎使用的信息源有几种,包括JRockit
Hot Spot Detector(优化引擎也使用它来决定应该优化哪些方法)、操作系统、JRockit
Memory System(最出名的就是垃圾收集器)和JRockit锁定分析器(如果支持的话)。
JRockit Memory Leak Detector
虽然Java的自动内存管理机制把开发人员从显式地分配和释放所使用内存的重担下解放出来,但如果程序继续引用不再有用的对象时,内存泄漏还是有可能发生。JRockit
Memory Leak Detector工具用来发现和查找内存泄漏原因。趋势分析器为用户提供了一个趋势分析,可以发现非常缓慢的泄漏,显示详细的堆统计信息(包括指向泄漏对象和分配位置的引用类型和实例),可以说明应用程序中每个类使用堆空间的情况,显示某一类型的实例使用了多少空间、它们占用了堆的哪一部分、存在多少个实例以及每秒钟堆空间使用的增加速度(以字节为单位),并快速找出泄漏原因。使用先进的图形化表现技术,以便更容易定位和理解有时比较复杂的信息。
JRockit Memory Leak Detector还提供快速找出泄漏原因的手段。可以在趋势分析表中选择一个怀疑类型,所有具有指向选中类型的实例的类型都可以显示在一个图中。图形节点可以随意展开,用户可以回溯到导致引用的最终原因。类的实例可以被显示,指向一个选中实例的所有实例都可以在一张实例图中显示出来。可以跟踪某个类的所有分配情况。
下载安装
JRockit Mission Control 2.0(内部称为Energy)是作为一组Eclipse插件而开发的,这个可以通过下载安装后,运行它之后所见到的程序风格以及帮助里的信息得知,当然从安装后的目录结构里我们也可以看到。Energy改进了用户界面,并利用了新的JRockit和6.0特有的管理特性,可以将JRockit
Mission Control 2.0与JDK 1.4、5.0和6.0版本的JRockit(版本号大于R27,目前这些JDK的版本号都到了R27.2.0)连接起来,因此可以无缝地进行连接。JDK及JRockit
Mission Control可以从http://commerce.bea.com/products/weblogicjrockit/jrockit_prod_fam.jsp
去选择相应的所需版本进行安装使用。各个版本的JDK里所附带的JRockit Mission
Control都是相同的版本2.0.1,本文以它来连接JRockit 5.0的JDK为示例进行说明。
申请许可
虽然JRockit Mission Control 2.0已经包含在BEA JRockit
R27.1以上版本的JDK里面了,即安装后就有了。但是,为了使用它,你还需要下载一个适合与你的许可。BEA提供两种类型许可:免费的有限制的开发者许可和收费的无限制的企业许可。许可下载地址为
http://dev2dev.bea.com/jrockit/tools.html
许可的安装,默认的JRockit JDK安装后带有一个空的JRMC许可license.bea,将下载到的许可文件拷贝到
%JROCKIT_HOME%/jre/ 目录下覆盖掉原来的文件,注意这个许可的文件名称必须是license.bea,这样许可的全路径就是
%JROCKIT_HOME%/jre/license.bea 了。但是,如果你已经有了JRMC之外的其它许可,你需要打开下载到的话可文件,将里面的component="JRA"和component="Memory
Leak Detector"的<license>拷贝到已有的许可文件里并保存即可。
使用
启动代理开始连接
要使用JRockit Management Console来监控JRockit JVM,首先必须启动要监控的JVM中的管理代理。也就是说应用系统必须是用JRockit
JDK进行启动的,并且启动了希望监控的JRockit的JMX管理代理服务。可以通过使用JRCMD或Ctrl-Break
Handler来启动和关闭管理代理,最简单的方法是直接借助于-Xmanagement标志来启动它。如java
-Xmanagement [your application]。但是通过JRCMD或Ctrl-Break
Handler来进行管理更加方便与无侵入,连启动的命令都不用个性。有关JRCMD或Ctrl-Break
Handler以及-Xmanagment选项的更多信息,请参见Management Console文档和Sun的JMX监控与管理的文档。
我们首先要在管理控制台中创建一个连接,连接至我们感兴趣的 JVM ,并确保系统正在运行且没有过载。然后,选中连接并点击管理控制台中的“Start
Memleak”图标,将启动内存增长的一个趋势(Trend)分析。 JVM 中每次进行垃圾收集时,
JRockit 都会把数据发送给位于一个趋势分析表中的 Memory Leak Detector,如下图所示,你可以根据自己的需要点击标题栏进行升降序排列,找出你需要进一步监测的对象。或者在下面的“Type
filter”里输入过滤条件,缩小你的监测范围。
趋势分析挖掘源头
对趋势进行分析,通过在每次垃圾收集时计算每个类的现有对象的数目,如果特定类的对象数目随时间而增长,就可能发生了内存泄漏。泄漏可能像细流一样非常小,所以趋势分析必须运行很长一段时间。在短时间内,可能会发生一些类的局部增长,而之后它们又会跌落。但是趋势分析的开销很小(最大开销是在每次垃圾收集时将数据包由JRockit发送到Memory
Leak Detector)。开销不应该成为任何系统的问题——即使是一个全速运行的生产环境中的系统。起初数目会跳跃不停,但是一段时间之后它们就会稳定下来,并显示出哪些类的数目在增长。
选中想要关注的Type,点击右键,在弹出的菜单中选择“Show Referring Type”,切换到“Type”标签并显示引用的关系图,如下图,(注意:以下示例的图示只是作为说明之用,与实际的应用系统并不相符。)
在图上选中关注点,并右键,可以根据不同的类型选择“List Instances” 在下方Instances里或选择“List
Largest Arrays”在下方的Largest Arrays里会列出所有的实例。
接下来,可以选中某一实例,右键,在弹出的菜单中选择“Inspect Instance”,切换“Instances”标签,并显示实例间的引用关系,并且在下方的“Inspector”窗口中显示具体实例的详细信息,可以很方便地点击左边的+号来逐级地展开进行观察,也可以通过右键的“Refresh”菜单实时地更新实例的信息。
在Instances Graph上,可以根据关注点,选中它,右键,在弹出的菜单中,选择“Show
Allocation Traces”,切换到“Allocation Stack Traces”标签,可以根据百分比的大小直接定位到你的源码代去,抓到源头了就可以采取相应的方法进行解决问题。
限于篇幅,以上仅简单介绍Memory Leak Detector的使用过程,更加丰富的以及Runtime
Analyzer、Management Console功能可以从附带的帮助文档中获取。
总结
简单地说,JRockit是汽车的引擎,而JRockit Mission Control是用于调优和维护引擎的工具和设备箱。
当前用于监控、管理和分析Java运行时的大部分技术都使用了相当具有侵入性的技术,比如字节码装置和JVMPI(现已废弃不用并被JVMTI所取代)。JRMC主要关注在完成必需的工具检测的同时对运行的系统造成最小的影响。它所使用的技术还使得工具与JVM断开连接之后应用程序立刻可以以全速运行。因此,JRMC适用于在生产环境中使用。它的开销非常小,这可以将Heisenberg效应降至最低,并为应用程序提供比其他开销更大的技术更有代表性的数据。
JRockit JVM 拥有一些用于实时内存泄漏检测的独特功能。现在的JRockit JDK包含一个多用途的工具套件,可用于进行监控、管理、分析和消除应用程序中的内存泄漏。JRMC可免费用于开发。它可以可靠地用在生产环境中,而且在使用之后,也不会在系统中留下任何痕迹。在实际应用中,它所引起的性能开销要比其他工具小的多。
本文仅是简要介绍一下这个套件的功能,关于JRMC的更多信息请见其帮助与BEA网站的文档。
最后,作为一名Java开发人员,应当始终关注应用程序的性能问题;同时也应该同能够帮助提高应用程序性能的人(如DBA,其实这经常是最重要的一个环节)一起协作;并尽可能地去关注Java性能方面的一些最佳实践。比如:可以经常访问Java
Performance Tuning
http://www.javaperformancetuning.com/
参考资料:
JROCKIT
MISSION CONTROL简介,Marcus
Hirt