监控的和管理的Java SE 6平台应用

来源:互联网 发布:青岛seo排名优化 编辑:程序博客网 时间:2024/05/16 12:29

http://www.sun.com/bigadmin/hubs/multilingual/simp_chinese/content/monitoring.jsp

 

BigAdmin System Administration Portal
监控的和管理的Java SE 6平台应用

Print-friendly VersionPrint-friendly Version

Submit Link or Content to BigAdmin | Tag This Page

 

翻译免责声明:
这篇文章由计算机软件程序翻译,并进行了一些人工处理和后编辑,以为读者提供方便。这篇翻译文章“按原样”提供,对于翻译文本或页面中所有内容的正确性或完整性,Sun 公司不作任何陈述或保证,也不承担任何责任。有关使用本网站和工具的其他条款和条件,请参阅本网站的《使用条款》。

 
 

a.l10n_selector_icon {border: 1px solid #E76F00;background-color:#fff;padding: 4px 0px 0px 2px;margin-left:3px;}a.l10n_selector_icon:hover {background-color: #FFC726;text-decoration:none;}#l10n_selector_block{float:right;padding:2px;margin:3px 3px 6px 3px;clear:right;display:none;}
Compare Translations

文章索引

应用程序比预期的运行速度慢或比以前运行地慢,或者这个应用程序无响应或挂起。您在生产或在开发期间可能遇到这些情况。这些问题的根源是什么?通常,起因 -- 例如内存泄漏、死锁和同步问题 -- 是很难诊断的。第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选项
 
对象被意外引用
jconsolejmapjhat
参见jmap -dump选项
Finalizer
对象挂起,无法完成
jconsole
jmap -dumpjhat
死锁
对象监视器或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工具诊断共同的JavaSE问题。JDK工具使您得到关于应用程序的更多诊断信息和帮助,以便确定应用程序是否按预想的正常运行。在某些情况下,诊断信息可能很充足,能够帮助您诊断问题和辨认它的起因。其他情况下,您可能需要使用分析工具或调试器来调试问题。

关于每个工具的详情,参见Java SE 6工具文献。

诊断内存泄漏的方法

内存泄漏可能需要很长时间才能再现,特别是在它仅发生在非常罕见或稀少的情况下时。理想情况下,开发人员会在OutOfMemoryError发生前先诊断到内存泄漏。

首先,使用JConsole监测内存使用是否连续地增长。这是可能的内存泄漏的征兆。图1显示连接到应用程序MemLeak的JConsole内存选项卡,图中显示内存使用持续增长。您还可以在内存选卡的插入框中观察到垃圾回收(GC)活动。

图1 :内存选项卡显示内存使用持续增长,这是可能的内存泄漏的征兆。
图1:内存选项卡显示内存持续增长,这是可能的内存泄漏的征兆。

您也能使用jstat命令监测如下内存使用和垃圾回收统计数据:

  $ <JDK>/bin/jstat -gcutil <pid> <interval> <count>

jstat - gcutil选项打印所运行应用程序进程ID <pid>在指定抽样间隔<interval>下,<count> 次的堆使用及垃圾回收时间摘要。这产生出以下样例输出:

  S0     S1     E      O      P     YGC   YGCT    FGC    FGCT     GCT
0.00 0.00 24.48 46.60 90.24 142 0.530 104 28.739 29.269
0.00 0.00 2.38 51.08 90.24 144 0.536 106 29.280 29.816
0.00 0.00 36.52 51.08 90.24 144 0.536 106 29.280 29.816
0.00 26.62 36.12 51.12 90.24 145 0.538 107 29.552 30.090

关于得到各种各样的VM统计的jstat输出和其他选项的详情,参见jstat手册页。

堆直方图

当您怀疑应用程序出现内存泄漏时, jmap命令将帮助您得到显示每类统计的堆直方图,包括事例的总数和每类事例所占的总字节数。使用以下命令行:

$ <JDK>/bin/jmap -histo:live <pid>
 

堆直方图的输出看起来与下例相似:

num   #instances    #bytes  class name
--------------------------------------
1: 100000 41600000 [LMemLeak$LeakingClass;
2: 100000 2400000 MemLeak$LeakingClass
3: 12726 1337184 <constMethodKlass>
4: 12726 1021872 <methodKlass>
5: 694 915336 [Ljava.lang.Object;
6: 19443 781536 <symbolKlass>
7: 1177 591128 <constantPoolKlass>
8: 1177 456152 <instanceKlassKlass>
9: 1117 393744 <constantPoolCacheKlass>
10: 1360 246632 [B
11: 3799 238040 [C
12: 10042 160672 MemLeak$FinalizableObject
13: 1321 126816 java.lang.Class
14: 1740 98832 [S
15: 4004 96096 java.lang.String
< more .....>
 

jmap - histo选项请求所运行应用程序进程ID <pid>的堆直方图。您可以指定live子选项,以便jmap只计入堆中的活动对象。要计入所有对象(包括不能获得的对象),使用以下命令行:

$ <JDK>/bin/jmap -histo <pid>
 

有时通过比较两张堆直方图可以得出哪些对象是垃圾对象,这很有用处,两张图中一张计入所有对象(包括不能得到的对象),另一张只计入活动对象。从一个或多个堆积直方图快照,您能尝试辨别出内存泄漏,它们通常有以下特征中的任何一个:

  • 其实例占用异常大的内存。
  • 类实例的数量以非常高的速率随着时间的推移增长。
  • 您以为会是要回收垃圾的类实例并不是垃圾。

前面jmap实用工具得到的堆直方图表明LeakingClass和它的数组有最大的实例计数,因此它有泄漏嫌疑。

堆直方图有时可以提供您诊断内存泄漏所需的信息。例如,如果应用程序仅在几个地方使用泄漏类,便能容易地找出泄漏原代码。另一方面,如果泄漏类使用非常广泛,例如java.lang.String类,您将需要追踪对象的引用并通过分析堆转储来做进一步诊断。

堆转储

您可以用以下任何一种方式获得堆转储。首先,您可以使用jmap命令借助以下命令行得到堆转储:

$ <JDK>/bin/jmap -dump:live,file=heap.dump.out,format=b <pid>
 

这产生出以下样例输出:

Dumping heap to d:/demo/heap.dump.out ...
Heap dump file created
 

jmap -dump选项要求将运行中应用程序进程 <pid>的堆转储写入指定的文件heap.dump.out。与- histo选项类似,live子选项是可选的,它用于指定仅转储Live对象。

第二种方法是通过调用HotSpotDiagnostic MBean的dumpHeap操作从JConsole中获得堆转储,如图2所示。

图2 :通过调用HotSpotDiagnostic MBean的<code>dumpHeap</code>操作获得堆转储。
图2:通过调用HotSpotDiagnostic MBean的dumpHeap操作获得堆转储。
 

在您使用JConsole监视应用程序时,这种方法是特别方便有用,因为您可以用单一工具进行监测和故障排除。另外, JConsole允许您远程连接到应用程序,因而您可以从另一台机器中请求堆转储。

现在您已了解了两种于运行时获得堆转储的方法。您还可以通过设置HeapDumpOnOutOfMemoryError HotSpot VM选项,请求在第一次抛出OutOfMemoryError时创建堆转储。启动应用程序时,您可以在命令行中设置这个选项:

$ <JDK>/bin/java -XX:+HeapDumpOnOutOfMemoryError ... 
 

此选项还可以通过使用jinfo命令,在应用程序运行时进行设置。

$ <JDK>/bin/jinfo -flag +HeapDumpOnOutMemoryError <pid>
 

最后,您可以通过调用 HotSpotDiagnostic MBean的setVMOption操作,用JConsole设置HeapDumpOnOutOfMemoryError选项,如图 3 所示。

图3 :通过调用HotSpotDiagnostic MBean的<code>setVMOption</code>操作设置VM选项。
图3:通过调用HotSpotDiagnostic MBean的setVMOption操作设置VM选项。
 

在抛出OutOfMemoryError时,将自动创建名为java_pid<pid>.hprof的堆转储文件:

java.lang.OutOfMemoryError: Java heap space
Dump heap to java_pid1412.hprof ...
Heap dump file created [68354173 bytes in 4.416 secs ]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at MemLeak.consumeMemory(MemLeak.java:25)
at MemLeak.main(MemLeak.java:6)
 
堆分析

一旦您具有了堆转储,即可以使用jhat命令进行堆分析和确定哪些引用使泄漏嫌疑一直存在:

$ <JDK>/bin/jhat heap.dump.out
 

这产生出以下样例输出:

Reading from heap.dump.out...
Dump file created Tue Jul 20 12:05:59 PDT 2006
Snapshot read, resolving...
Resolving 283482 objects...
Chasing references, expect 32 dots..........................................
Eliminating duplicate references............................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
 

jhat实用程序(原HAT堆分析工具)读取堆转储,并在指定的端口上启动http服务器。然后您可以使用任何浏览器连接到服务器,并且针对指定的堆转储执行查询。图4显示了jhat分析的堆转储中除java.*javax.*之外的所有类。这个工具支持许多查询,其中包括:

  • 显示从根集到给定对象的所有引用路径。此查询对发现内存泄漏特别有用。
  • 显示所有类的实例计数。
  • 显示包括所有类的实例计数和大小的堆直方图。
  • 显示finalizer摘要。
图4 :堆转储显示除<code>java.*</code>和<code>javax.*</code>之外的所有类。
图4:堆转储显示除java.*javax.*之外的所有类。
 

您也可以用内置的对象查询语言 (OQL)接口开发自己的自定义查询以深入剖析具体的问题。例如,如果您想要查找所有字符串长度超过100的java.lang.String对象,可以在OQL 查询页中输入以下查询:

select s from java.lang.String s where s.count >= 100
 
诊断Finalizer被过分使用的方法

对finalizer的过分使用会保留内存,并阻止应用程序快速回收该内存。这种过分使用可能导致OutOfMemoryError。如图5所示,您可以使用JConsole监测待完成的暂挂对象的数量。

图5 :JConsole中的VM选项卡显示待完成的暂挂对象的数量。
图5:JConsole中的VM选项卡显示待完成的暂挂对象的数量。
 

您也可以使用早先描述的jhat找出堆转储中有哪些可完成的对象。

另外,在Solaris和Linux操作系统上,您可以使用jmap实用程序查找可完成对象的类:

$ <JDK>/bin/jmap -finalizerinfo <pid>
 
诊断死锁的方法

Java SE 6提供了两种非常方便的方式,可以发现应用程序中是否出现了死锁,并且还增强了死锁检测工具,以支持java.util.concurrent锁。JConsole和jstack命令都可以查找与对象监视器有关的死锁 -- 即使用synchronized关键字获得的锁 -- 或java.util.concurrent 可拥有同步器的死锁。

图6显示在Deadlock应用程序中有两个死锁,死锁2选项卡显示在对象监视器上被阻拦的三个死锁线程。每个死锁选项卡显示包含死锁的线程,标识线程在哪个锁上被阻拦,并且指明哪个线程拥有该锁。

图6 :JConsole查出两个死锁,并提供相关细节。
图6:Console查出两个死锁,并提供相关细节。
 

您也可以使用jstack实用程序得到线程转储和查出死锁:

$ <JDK>/bin/jstack <pid>
 

以下是jstack样例输出的下半部分,该输出查出了一个涉及java.util.concurrent可拥有同步器的死锁。

jstack样例输出
点击此处查看清晰样例。
诊断循还线程的方法

不断增长的CPU使用量是循还线程的一个征兆。JTop是显示应用程序每个线程的CPU时间使用情况的JDK演示。JTop按CPU使用量排序线程,从而允许您轻松查出CPU时间量非常高的线程。如果高线程CPU消耗与预期行为不符,那么该线程可能是循还线程。

您可以将JTop作为独立的GUI运行:

$ <JDK>/bin/java -jar <JDK>/demo/management/JTop/JTop.jar
 

另外,您也可以将它作为JConsole的插件运行:

$ <JDK>/bin/jconsole -pluginpath <JDK>/demo/management/JTop/JTop.jar 
 

此操作启动带有附加JTop选项卡的JConsole工具,该选项卡显示应用程序中每个线程在使用的CPU时间,如图7所示。JTop选项卡显示LoopingThread使用的高CUP时间量在不断增加,这即是一个疑点。开发人员应该审查该线程的源代码,以查看它是否包含死循环。

图7 :JTop选项卡显示应用程序中的每个线程使用了多少CPU时间。
图7:JTop选项卡显示应用程序中的每个线程使用了多少CPU时间。
诊断严重锁争用的方法

确定哪些锁是瓶颈的过程可能相当困难。JDK提供了每个线程的争用统计(如对象监视器上的锁阻拦或等待次数)及锁争用中花费的总累积时间。有关线程在对象监视器上阻拦或等待次数的信息,总是可以从JConsole线程选项卡中显示的线程信息中找到,如图8所示。

图8 :线程选项卡显示线程在对象监视器上阻拦或等待的次数。
图8:线程选项卡显示线程在对象监视器上阻拦或等待的次数。
 

但在默认情况下,跟踪线程争用中所用总累积时间的能力是被禁用的。您可以通过将Threading MBean的ThreadContentionMonitoringEnabled属性设置为true来启用对线程争夺时间的监视,如图9所示。

图9 :通过设置Threading MBean的<code>ThreadContentionMonitoringEnabled</code>属性启用对线程争夺的监视。
图9:通过设置Threading MBean的ThreadContentionMonitoringEnabled属性启用对线程争夺的监视。
 

您可以检查线程争用统计,以确定线程是否具有高于预期值的锁争用现象。您可以通过用线程ID作为输入参数来调用Threading MBean的getThreadInfo操作,取得线程阻拦的总累积时间,如图10所示。

图10 :这是Threading MBean的<code>getThreadInfo</code>操作的返回值。
图10:这是Threading MBean的getThreadInfo操作的返回值。
 
总结

Java SE 6平台提供了多种监视和管理工具,允许您诊断生产和开发环境中Java SE应用程序的共同问题。JConsole允许您观察应用程序和检查症状。另外, JDK 6包括几种其他的命令行工具。jstat命令打印各种VM统计数据,其中包括内存使用情况和垃圾回收时间。jmap 命令允许您获得运行时的堆直方图和堆转储。jhat命令允许您分析堆转储。jstack命令允许您获得线程堆栈跟踪。这些诊断工具可以附加到任何应用程序,不需要以特别方式启动。借助这些工具,您可以更加高效地诊断应用程序中的问题。

原创粉丝点击