监控的和管理的Java SE 6平台应用
应用程序比预期的运行速度慢或比以前运行地慢,或者这个应用程序无响应或挂起。您在生产或在开发期间可能遇到这些情况。这些问题的根源是什么?通常,起因 -- 例如内存泄漏、死锁和同步问题 -- 是很难诊断的。第6版Java平台,标准版(Java SE)提供了现成的监视和管理功能,以帮助您诊断许多共同的Java SE问题。
这篇文章是监视和管理Java SE 6应用程序的一个简短课程。它首先描述Java SE应用程序中共同的问题和它们的症状。其次,它提供了Java SE 6的监视和管理功能概要。最后,它描述如何使用各种各样的Java开发套件 (JDK)工具诊断这些问题。
注:针对Java SE平台规范的所有API附加项或其他改进都需要经过JSR 270专家组的审核和批准。
Java SE应用程序共同的问题
?
?
一般说来,Java SE应用程序中的问题与重要资源(例如内存、线程、类和锁)有关。资源冲突或泄漏可能导致性能问题或意想不到的错误。表1总结了一些Java SE应用程序中共同的问题和它们的症状,并列出开发人员可以用来帮助诊断每个问题的来源的工具。
?
表 1. 为诊断共同的问题使用的工具
?
?
?
问题
症状
诊断工具
?
内存不足
OutOfMemoryError
Java 堆分析工具 (jhat)
内存泄漏
内存的使用增长
频繁的无用单元回收
Java监视和管理控制台(jconsole)
JVM统计监视工具(jstat)
?
类的增长率很高
类的实例数异常
内存图(jmap)
参见jmap -histo选项
?
对象被意外引用
jconsole或jmap与jhat
参见jmap -dump选项
Finalizer
对象挂起,无法完成
jconsole
jmap -dump与jhat
死锁
对象监视器或java.util.concurrent锁上的线程阻拦
jconsole
堆栈检索(jstack)
循环线程
线程CPU时间连续地增加
jconsole与JTop
严重的锁争用
线程的争用统计值很高
jconsole
?
内存不足
Java虚拟机(JVM)*有以下类型的内存:堆内存、非堆内存和本机内存。
堆内存是为所有类实例和数组分配内存的运行时数据区域。非堆内存包括对JVM进行内部处理或优化所需的方法区域和内存。它存放每个类的结构,例如一个运行时常数池、字段和方法数据,以及方法和构造函数代码。本机内存是操作系统处理的虚拟内存。当内存不足,无法分配给应用程序时,即抛出 java.lang.OutOfMemoryError。
以下错误信息即为每种类型的内存可能抛出的 OutOfMemoryErrors 错误消息:
?????? 堆内存错误。当应用程序创建一个新的对象,但是堆没有充足的空间,并且不可能进一步被扩展时,将抛出 OutOfMemoryError 与以下错误信息:
?
java.lang.OutOfMemoryError: Java heap space
?
?????? 非堆内存错误。 永久保存区是HotSpot VM 实现中的一个非堆内存区,用于存放每个类结构和驻留的字符串。当这个永久保存区充满时,这个应用程序将不能装载类或分配驻留的字符串,并且会抛出OutOfMemoryError与以下错误信息:
?
java.lang.OutOfMemoryError: PermGen space
?
?????? 本机内存错误。Java本机接口 (JNI)代码或应用程序的本机库以及JVM实施从本机堆分配内存。当分派在本机堆发生故障时,抛出 OutOfMemoryError。例如,以下错误信息表明交换空间不足,这可能由于操作系统的配置问题所致,或由于系统中的另一个进程消耗过多内存:
?
java.lang.OutOfMemoryError: request <size> bytes for <reason>.
Out of swap space?
?
内存不足问题可能源于配置问题 -- 应用程序真地需要这么多内存 -- 或是由于应用程序的性能问题,需要您分析和优化该程序以减少内存使用。配置内存设置和分析应用程序以减少内存使用超出了这篇文章的范围,您可以参考HotSpot VM 内存管理白皮书 (PDF)获取相关信息,或使用诸如NetBeans IDE Profiler 之类的分析工具。
内存泄漏
JVM负责自动管理内存,为应有程序索还尚未使用的内存。然而,如果应用程序持续引用它不再需要的对象,这个对象便不能被垃圾回收,它将继续占用空间,直到被删除为止。这种无意识的对象保留称为内存泄漏。如果应用程序泄漏很多内存,它最终将用尽内存,并且抛出OutOfMemoryError。另外,垃圾回收也会常常发生,因为应用程序尝试释放空间,因而造成应用程序运行减慢。
Finalizer
OutOfMemoryError的另一个可能原因是对finalizer的过分使用。java.lang.Object类有一个被保护的方法叫finalize。类可以忽略此finalize方法,以在该类的对象被垃圾回收之前处理系统资源或执行清理。可以被对象调用的finalize方法称为该对象的finalizer。不保证finalizer何时运行,也不保证它可以运行。有finalizer的对象在其 finalizer 运行之前都不会被垃圾回收。因此,为最终完成而挂起的对象将保留内存,即使对象不再被该应用程序引用也是如此,这还可能导致与内存泄漏相似的问题。
死锁
当两个或多个线程都等待另一个线程释放锁时,就会发生死锁。Java编程语言使用监视器来同步线程。每个对象都同一个监视器联系在一起,也被称为对象监视器。如果线程在对象上调用一个synchronized方法,则该对象被锁定。另一个线程若在此相同对象上调用synchronized方法,则会被阻拦,直至锁被释放为止。除内置的同步支持以外,在J2SE 5.0中引入的java.util.concurrent.locks包为锁定和等待条件提供了一个框架。死锁可能涉及对象监视器和java.util.concurrent锁。
一般情况下,死锁会导致这种应用程序或它的一部分变得无响应。例如,如果负责图形用户界面 (GUI)更新的进程被锁死, GUI应用程序则会冻结,并且不响应任何用户动作。
循环线程
循环线程也可能造成应用程序挂起。当一个或多个线程在一个死循环中执行时,这个循环也许会消耗所有可利用的CPU周期并造成这种应用程序的其余部分无响应。
严重的锁争用
同步在多线程应用程序中大量使用,这是为了保证对一种共享资源的独占访问或为了在多个线程间协调和完成任务。例如,应用程序在数据结构上使用一台对象监视器同步更新。当两个线程试图同时更新数据结构时,只有一个线程能获取对象监视器和继续更新数据结构。同时,另一个线程被阻拦,等待进入synchronized块,直到第一个线程完成它的更新并释放对象监视器。同步争用情况会影响应用程序的性能和可扩展性。
Java SE 6平台的监视和管理能力
?
?
Java SE 6 中的监视和管理支持包括编程接口以及几个有用的诊断工具,用以检查各种各样的虚拟机 (VM)资源。关于编程接口的信息,请阅读API规范。
JConsole是允许您监测各种各样的VM资源运行时使用情况的Java监视和管理控制台。它使您注意到前面部分描述的应用程序执行过程中出现的症状。您可以使用JConsole连接到在同一机器上本地运行的应用程序或在不同机器上远程运行的应用程序,监测以下信息:
?????? 内存使用和垃圾回收活动
?????? 线程状态、线程堆栈检索和锁
?????? 等待最终完成的对象数目
?????? 运行时信息,例如正常运行时间和进程消耗的CPU时间
?????? VM信息,例如JVM的输入参数和应用程序类路径
另外, Java SE 6还包括其他命令行实用工具。jstat命令打印各种各样的VM统计数据,包括内存使用、垃圾回收时间、类加载和及时编译器统计。 jmap 命令允许您获得运行时的堆直方图和堆转储。jhat命令允许您分析堆转储。jstack命令允许您获得线程堆栈跟踪。这些诊断工具可以附加到任何应用程序,不需要以特别方式启动。
用JDK工具诊断
?
?
这个部分描述如何使用JDK工具诊断共同的Java SE问题。JDK工具使您得到关于应用程序的更多诊断信息和帮助,以便确定应用程序是否按预想的正常运行。在某些情况下,诊断信息可能很充足,能够帮助您诊断问题和辨认它的起因。其他情况下,您可能需要使用分析工具或调试器来调试问题。
关于每个工具的详情,参见Java SE 6工具文献。