JVM 提供许多统计信息。这些统计信息涉及诸如即时 (JIT) 编译、类加载、内存分配以及最有趣的垃圾收集之类基本的 JVM 特性。
JVM 可将字节代码即时编译为机器代码。该动作类似于像您用于脚本语言的解释器(如 Perl)所执行的动作。但是它更先进,提供许多优化措施,使得已解释的字节代码几乎与预编译的代码运行得一样快(有时更快)。显然,执行 JIT 编译需要开销。度量应用程序需要多大开销的两个有用的统计信息是 JIT 编译次数和执行这些编译花费的时间总量。(有关 JIT 编译的更多详细信息,请参阅 参考资料。)
JVM 负责从应用程序的类使用的库向应用程序和类中加载类。因此,这些类可能是从已部署在 Geronimo 中的 WAR 或 EAR 中加载的类,也可能是包含在 WAR 或 EAR 中的 JAR 文件,或者是由 Geronimo 容器加载的 JAR 中的类。如果类不用于延长的时间,则 JVM 还可以选择卸载这些类。这将导致几项重要的统计信息:加载的类的数量、卸载的类的数量、加载和卸载类花费的时间。
在 Java 技术中没有 malloc() 函数,这是因为 JVM 可自动为对象分配内存。对象从堆中分配;因此,使用的和空闲的堆内存数量是两个非常重要的统计信息。监视堆分配是检测可怕的内存泄漏最简单的方法之一。
这可能是 JVM 最有趣的统计信息。正像 JVM 为对象分配内存一样,它还回收不再使用的对象上的内存。关于垃圾收集如何工作,包括您可以指示 JVM 用于其垃圾收集器的不同算法,有很多有趣的读物。
还有许多关于垃圾收集器本身的有趣的统计信息。首先是调用垃圾收集器的频率和垃圾收集过程中花费的时间。垃圾收集的开销很大,因为本质上它会中止应用程序的执行以便可以检验对象。显然,大量的垃圾收集可以真正减慢应用程序的运行速度。
其他有趣的统计信息与对象分代有关。垃圾收集器按代对对象进行分类,每代都指示对象已经经历了一定次数的垃圾收集。(经历很多次垃圾收集的对象比未经历或者经历了很少次数垃圾收集的对象被垃圾收集的可能性更小。)这样的对象分类允许垃圾收集器检验更少的对象,使每次垃圾收集速度更快。因此,每代中对象的数量可能相当有趣,提供更好的方法来查找内存泄漏和很好的方法来考察潜在优化(如对象缓存和对象池)的影响。
有几个可用的 JVM 实现,但最常见的是 Sun 的 HotSpot JVM(有关技术链接,请参阅 参考资料)。从 Java 2 Platform, Standard Edition (J2SE) 1.4.2 开始,HotSpot 进行了全面配备,提供许多上面描述的有用的统计信息。有好几个方法用于获得和分析这一数据。获得 JVM 生成的统计信息之后,您可以访问 Geronimo 的 JVM 并分析您的应用程序。
获得一些 JVM 统计信息的最简单的方法是通过命令行。HotSpot 识别许多命令行选项,其中几个选项允许您定制堆大小和垃圾收集选项。也可以使用 -verbose:gc 选项。这可使 JVM 打印出以下形式的行:
[GC 70333K->65666K(98896K), 0.0007817 secs]
[Full GC 65666K->59333K(98896K), 0.0205250 secs]
上面示例中的第一行指示发生了一次垃圾收集。收集之前堆上使用了 70,333KB,收集之后使用了 65,666KB。第一行还指示总共有 98,896KB 的可用空间。最后,指出垃圾收集花费的时间,也即应用程序中止的时间量。第二行指示相同的信息,但表示一个完整的垃圾收集。完整的垃圾收集涉及 JVM 对堆上所有的对象进行检验,包括经历了许多代、在部分垃圾收集上不会被分析的对象。
有关更详细的信息,请使用 -XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps 选项。这些选项提供关于堆上对象的不同代的详细信息。
对于看上去更为有趣的统计信息,可以使用专门的工具。开放源码的工具和商业工具都可以使用。还可以使用 Sun 提供的一些用于与 HotSpot 一起使用的工具。jvmstat 项目包含几个用于监视 JVM 统计信息的工具,包括一个叫做 Visual Garbage Collector 或 visualgc 的图形工具, 它使用起来相对简单。您需要 Java 5 或更高版本来运行 jvmstat,尽管它可以附加和监视 1.4.2 或更高版本的 JVM。只需下载和解压即可(有关下载链接,请参阅 参考资料)。将它添加到您的路径,即可准备运行。启动了 Java 进程时,请使用 jps 工具来通过键入 jps 标识其 JVM。它向您提供一个正在运行的 Java 进程的列表,这些进程以其 JVM 作为 ID。然后使用您想监视的进程的 ID 调用 visualgc。jvmstat 发行版包括一个用于调用 visualgc 的 shell 脚本。如果安装了 cygwin 这样的工具,就可以在 *nix 或 Microsoft® Windows® 上调用 visualgc 了。此外,也可以按以下方式调用它:
java -Xbootclasspath/p:%JAVA_HOME%\lib\tools.jar -jar %JVMSTAT_HOME%\jars\visualgc.jar 316
其中 JAVA_HOME 环境变量指定安装 JDK 的位置,而 JVMSTAT_HOME 指示安装 jvmstat 包的位置。该行末尾的 316 是您想监视的 JVM 的 ID。该 jvmstat 包还包括一个 jstat 工具。它提供在 visualgc 中看到的绝大部分相同信息,只不过将其作为文本输出。这对收集统计信息而言至关重要,然后可以将这些统计信息拖入另一个程序,以对其进行深入分析、生成有关它的报告,等等。本文集中讨论使用 visualgc 对这些统计信息进行可视化。
清单 1 提供一个短程序,您可以运行来测试使用 jvmstat 进行监视。
清单 1. 测试用 jvmstat 进行监视
StatGen.java
import java.util.ArrayList;
import java.util.List;
public class StatGen {
static final int MAX_BLOCK = 8*1024*100;
public static void main(String[] args) {
try{
int numLoops = 1;
if (args.length > 0){
numLoops = Integer.parseInt(args[0]);
}
System.out.println("#loops="+numLoops);
List<long[]> list = new ArrayList<long[]>(numLoops);
for (int i=0;i<numLoops;i++){
int sz = (int) (Math.random()*MAX_BLOCK);
long[] garbage = new long[sz];
if (sz % 5 == 0){
list.add(garbage);
}
System.out.println("Sleeping 0.5s");
Thread.sleep(500);
}
System.out.println("Done");
} catch (Throwable t) {
t.printStackTrace();
}
System.exit(0);
}
}
|
这是一个简单的程序。它使用了泛型,所以您需要 Java 5 。如果愿意,您可以容易地改变它以不使用泛型,然后它将用较旧的 JDK 运行。它通过创建可随机调整大小的长整数数组导致内存分配。它通过随机将这些数组的一部分(大约 20%)放入一个列表来模拟内存泄漏。因此,垃圾收集器可以回收在每个循环上分配的大多数内存,但它不能回收添加到列表中的数组。您可以试验一些参数,比如睡眠大小和最大块大小。它还可以接收命令行参数,所以您可以容易地告知它要执行多少次循环。
编译 StatGen 之后,您可以简单地从 java StatGen 100 开始。这执行 100 次循环。记住,您可以在命令行指定最小和最大堆大小。采用不同的最小和最大堆大小在运行 visualgc 时将产生截然不同的视觉效果。您还可以指定不同的垃圾收集算法;用 visualgc 监视 StatGen 时,将很好地理解这些算法的区别。
图 1 展示了一些用 StatGen 运行 visualgc 的示例输出。
图 1. 用
StatGen 运行 visualgc
图 2 展示了最有趣的窗口。它首先展示了 JIT 编译。在本例中,只有三个窗口,这对于这样一个简单的示例来说并不稀奇。它还展示了类加载器信息 —— 再说一遍,这很简单,因为应用程序太简单了。
图 2.基本的 JVM 信息和堆使用情况
接着,它展示了总体的垃圾收集统计信息。您会看到 123 次收集和进行垃圾收集所花费的总时间约为 0.3 秒。从第一个图形中,您可以看到应用程序运行了大约 100 秒,所以进行垃圾收集花费了该时间的 0.3%。
还有关于不同分代的统计信息。Eden 空间显示了从未通过垃圾收集的新对象。示例代码生成许多这样的新对象,并且它们很快就被收集掉。
注意两个 Survivor 空间。它们基本上是较小的经历过少数几次垃圾收集的对象集。所有对象要么是立即准备收集的,要么是从未准备收集的,所以,如果它们到了 Survivor 空间,就将很快改变这种状态。
接下来是较老的一代。这些对象经历了大量垃圾收集。您可以看到稳定的对象增加,表明存在潜在的内存泄漏。最后,您看到的是 JVM 确定永远不会被收集的对象的永久代。
直方图窗口(参见 图 3)展示了关于 Survivor 空间的数据 —— 此处列出的不多。
图 3. 展示关于 Survivor 空间的数据的直方图窗口
在您前面下载的二进制和源安装中有几个目录。二进制下载包括运行和使用 Geronimo 所必需的 Geronimo 的梗概。源下载包括所有 Geronimo 源代码,包括用于构建整棵树的 Maven 构建脚本。首先来看二进制发行版,然后再看源代码发行版。
Geronimo 附带了几个示例 Web 应用程序。既然您有了一些从 JVM 提取 JVM 统计信息的工具,就可以容易地为 Geronimo 应用程序执行此操作。
仅仅启动 Geronimo 并用 visualgc 监视它就相当有趣,如 图 4、图 5 和 图 6 所示。
图 4. 启动 Geronimo 并用
visualgc 进行监视
图 5. 展示 JIT 编译、类加载器信息和垃圾收集的图形
图 6. Survivor Age Histogram
用标准的 java -jar %GERONIMO_HOME%/bin/server.jar 命令启动 Geronimo。仅通过使用启动命令上的 -server 选项即可获得直接视图,如 图 7 所示。
图 7. 使用启动命令上的
-server 选项
注意,JIT 编译少得多。这是因为在您使用 -server 选项启动 JVM 时加载了更多的类。默认的启动模式设计用于更快的启动,以适应桌面应用程序。
在这两种情况下,您都会在堆上看到许多长时间运行的对象,但是一旦 Geronimo 完全启动,这个数量将趋于平缓。这展示所有正在被加载的服务器对象,如各种 GBeans 以及它们管理的服务。Geronimo 开发人员将因看不到内存泄漏的迹象而高兴,因为老的一代和永久的一代分配趋于平缓。
现在您也可以运行一些包含的 Geronimo Web 应用程序。图 8 中的图形展示 JSPX-XHTML 示例应用程序的垃圾收集动作。
图 8. JSPX-XHTML 示例应用程序的垃圾收集动作
您可以看到左侧的对象突然增多,然后大部分对象将在呈现该页面后消失,这与您所期望的一样。您可以看到也对应于应用程序运行的 JIT 编译和类加载事件。
既然了解了如何访问和分析运行 Geronimo 的某个示例 Web 应用程序时生成的 JVM 统计信息,就可以开始分析自己的应用程序了。大概就是启动 Geronimo、启动 visualgc,然后部署和/或启动您的应用程序。对它运行一些事件,观察它加载类、编译字节代码以及管理内存。
JVM 统计信息在调优应用程序时非常宝贵。Sun 提供的工具让您快速分析应用程序、执行优化并查看优化的效果。它们还允许试验多个 JVM 选项,以对应用程序执行附加调整。Geronimo 的目标之一是提供高性能应用服务器,而 JVM 统计信息的分析是寻求最高应用程序性能的一项重要补充。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 通过阅读 “Java 理论与实践: 垃圾收集简史”(developerWorks,2003 年 10 月),了解基本的垃圾收集理论和原理。
- 阅读 “Java 理论与实践: JVM 1.4.1 中的垃圾收集”(developerWorks,2003 年 11 月),了解分代垃圾收集。
- 使用 Java 5 和 Aspects 的特性 构建工具,进一步了解应用程序的性能(developerWorks,2006 年 3 月)。
- 获得 Java HotSpot VM 提供的各种选项的综合列表。该列表包括生成统计信息的选项和调整性能的选项。
- 务必阅读 “The Hotspot Virtual Machine”(关于 HotSpot 的深层次文章),它解释了像 JIT 编译这样的动作如何影响性能以及您如何使用此信息来调优应用程序。
- 访问 Java Performance Tuning resources 站点,该站点包含大量有关调优的资源,从图书到免费的和商业的工具一应俱全。
- 阅读 “构建更佳的 J2EE 服务器,开源之路”(developerWorks,2005 年 5 月),了解 Geronimo 的架构和高性能特性。
- 阅读 Geronimo renegade 系列 —— 它以全新的视角来看问题。
- 访问 developerWorks 开放源码专区,获得广泛的介绍信息、工具和项目更新,以帮助您用开放源码技术进行开发并且将它们与 IBM 产品结合使用。
- 查看 developerWorks Apache Geronimo 项目区域,获得文章、教程和其他资源,帮助您立即开始用 Geronimo 进行开发。
- 查看 IBM Support for Apache Geronimo 服务,使您开发受到世界一流 IBM 支持的 Geronimo 应用程序。
- 在 developerWorks 的 开始学习 Apache Geronimo 部分可找到针对初学者和有经验的用户的有用资源。
- 浏览 developerWorks 开放源码专区提供的所有 Apache 文章 和 免费 Apache 教程。
- 在 Safari 书店 浏览关于这些和其他技术主题的图书。
获得产品和技术
- 下载本文中使用的 jvmstat 包。
- 下载 Sun 的 HotSpot JVM。
- 下载 Java 5.0。
- 下载 Apache Geronimo, Version 1.0。
- 用 IBM 试用软件 革新您的下一个开放源码开发项目,这些软件可下载或通过 DVD 获得。
- 下载 IBM WebSphere® Application Server Community Edition V1.0 的免费版 —— 这是构建于旨在帮助您加快开发和部署工作的 Apache Geronimo 开放源码技术之上的轻量级 J2EE 应用服务器。
讨论
- 通过阅读 Apache Geronimo blog,及时了解 Geronimo 开发信息。
Michael Galpin 拥有加州理工学院的数学学位。从 20 世纪 90 年代末以来,他一直是 Java 开发人员兼 Vitria Technology 在加州桑尼维尔市的软件工程师。您可以发电子邮件到 mike.sr@gmail.com 与作者取得联系。