一篇Sun项目主页上介绍JConsole使用的文章,前段时间性能測试的时候大概翻译了一下以便学习,今天整理一下发上来。有些地方也不知道怎么翻,就保留了原文,可能还好理解点。呵呵,水平有限,翻的不好,大家多多包涵。
JConsole毕竟是JDK自带的东西,功能尽管没有一些商业软件那么强大,可是稳定性好。在大压力情况下也不会发生什么问题。并且。提供了相对全面的系统监控功能,还是值得一用的。
JConsole
JConsole是一个基于JMX的GUI工具。用于连接正在执行的JVM,只是此JVM须要使用可管理的模式启动。假设要把一个应用以可管理的形式启动,能够在启动是设置com.sun.management.jmxremote
。比如,启动一个能够在本地监控的J2SE的应用Java2Demo ,需输入下面命令:
JDK_HOME/bin/java -Dcom.sun.management.jmxremote -jar JDK_HOME/demo/jfc/Java2D/Java2Demo.jar JDK_HOME须要
是一个含有JDK5.0的文件夹。
要启动JConsole,执行 JDK_HOME/bin/jconsole 一个用于连接的对话框将会打开。 对话框的Local标签列出了全部本地正在执行的JVM。还包括进程的ID等信息。
Figure 2: Local Tab.
JConsole能够以三种方式连接正在执行的JVM:
- Local:使用JConsole连接一个正在本地系统执行的JVM,而且执行程序的和执行JConsole的须要是同一个用户。JConsole使用文件系统的授权通过RMI连接器连接到平台的MBeanserver上。这样的从本地连接的监控能力仅仅有Sun的JDK具有
- Remote:使用以下的URL通过RMI连接器连接到一个JMX代理:
service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
hostName填入主机名称, portNum为JMX代理启动时指定的端口。
JConsole为建立连接。须要在环境变量中设置mx.remote.credentials来指定username和password从而进行授权。
- Advanced:使用一个特殊的URL连接JMX代理。
普通情况使用自己定制的连接器而不是RMI提供的连接器来连接JMX代理,或者是一个使用JDK1.4的实现了JMX和JMX Rmote的应用。
当JConsole成功建立连接。它从连接上的JMX代理处获取信息。而且以以下几个标签页呈现信息。
- Summary tab. 监控JVM和一些监控变量的信息。
- Memory tab. 内存使用信息
- Threads tab. 线程使用信息
- Classes tab. 类调用信息
- VM tab. JVM的信息
- MBeans tab.全部MBeans的信息
MBeans tab展示了全部以一般形式注冊到JVM上的MBeans。MBeans tab同意你获取全部的平台信息,包含那些不能从其它标签页获取到的信息。注意,其它标签页上的一些信息也在MBeans这里显示。另外,你能够使用 MBeans标签管理你自己的应用的MBeans
使用MBeans Tab监控和管理MBean
注冊到JMX代理的平台或者应用的MBeans。能够通过MBeans标签获取。
比如,内存的MBeans如以下定义
public interface MemoryMXBean { public MemoryUsage getHeapMemoryUsage(); public MemoryUsage getNonHeapMemoryUsage(); public int getObjectPendingFinalizationCount(); public boolean isVerbose(); public void setVerbose(boolean value); public void gc(); }
内存的MBean包含四个属性:
HeapMemoryUsage
. 用于描写叙述当前堆内存使用情况的仅仅读属性NonHeapMemoryUsage
. 用于描写叙述当前的非堆内存的使用情况的仅仅读属性ObjectPendingFinalizationCount
.用于描写叙述有多少对象被挂起以便回收。Verbose
.用于动态设置GC是否跟着具体的堆栈信息,为一个布尔变量
Figure 3: MBeans Tab.
左边的树形结构以名字的方式展示了全部MBeans的列表。一个MBean对象的名字由一个域的名字和一串keyword属性组成。比如,JVM的平台的MBeans是在“java.lang”域下的一组,而日志的MBeans则在"java.util.logging
"域下。MBean对象的名字在javax.management.ObjectName 规范中定义。
当你在树中选中一个MBean。属性,方法,或者通知等一些信息会再右边显示出来。假设属性是可写的(属性被标志为蓝色),你能够进行设置。
你能够操作在Operations tab中列出的操作。
你也能够看到由MBean发送出来的通知:默认情况,假设你不订阅通知的话,JConsole不会收到MBean发生过来的通知。
你能够点击"Subscribe
"(订阅)button来堆通知进行定义,而使用"Unsubscribe
"button来取消订阅
Figure 4: MBeans Notification.
监控内存
内存标签页通过读取内存系统、内存池、垃圾回收的MBean来获取对内存消耗、内存池、垃圾回收的情况的统计。
上图展示了内存随时间变化的使用情况。有对堆的、非堆的以及特殊内存池的统计。内存池信息能否被获取。取决与使用的Java虚拟机。以下列表展示了HotSpot虚拟机的内存池情况。
Eden Space (heap): 内存最初从这个线程池分配给大部分对象。
Survivor Space (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
Tenured Generation (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。
Permanent Generation (non-heap): 保存虚拟机自己的静态(refective)数据。比如类(class)和方法(method)对象。Java虚拟机共享这些类数据。这个区域被切割为仅仅读的和仅仅写的,
Code Cache (non-heap):HotSpot Java虚拟机包含一个用于编译和保存本地代码(native code)的内存,叫做“代码缓存区”(code cache)
具体信息区域给出一些当前线程的信息:
Used :已使用:当前的内存使用量。使用的内存包含全部对象(能被获取和不能被获取的)所占用的内存。
Committed :分配量:Java虚拟机保证可以获取到的内存量。
分配内存(committedmemory)的量可能随时间改变。Java虚拟机可能释放部分这里的内存给系统,对应的分配的内存这时可能少于初始化时分配的给它的量。分配量总数大于或等于已使用的内存量。
Max :内存管理系统能够使用的最大内存量。
这个值能够被改变或者不做设定。
假设JVM试图添加使用的内存到大于分配量(committedmemory)的情况。内存分配可能失败,即便想使用的内存量小于或者等于最大值(如:系统虚拟内存比較低时)
Usage Threshold The usage threshold of a memory pool. This field will only beshown if the memory pool supports usage threshold.
GC time :垃圾回收使用的总时间和调用垃圾回收的次数。它可能有好几行,每行代表JVM使用的垃圾回收算法。
(
右下角的棒状图表显示了被JVM的内存池消耗的内存。假设内存使用超过 usage threshold,则棒会变红。
usagethreshold是用于支持内存检查的Memory Pool MBean的一个属性。MemoryPoolMXBean定义了一系列方法用于检查内存。
public interface MemoryPoolMXBean {
....
// Usage threshold
public long getUsageThreshold();
public void setUsageThreshold(long threshold);
public boolean isUsageThresholdExceeded();
public boolean isUsageThresholdSupported();
// Collection usage threshold
public long getCollectionUsageThreshold();
public void setCollectionUsageThreshold(long threshold);
public boolean isCollectionUsageThresholdSupported();
public boolean isCollectionUsageThresholdExceeded();
}
每种内存池可能有两种内存初始话支持: usage threshold和collection usage threshold特殊的内存池可能两种都不支持。
usage threshold是内存池中一个可管理的属性。它使用低负荷的内存监控。设置usage threshold为正值则usage
threshold检查内存池。设置usage threshold为零,则关闭检查。默认值由JVM设置。JVM一般让usage threshold在最合适的时候检查内存,典型的在GC的过程中和某些分配内存的时候。假设JVM发现当前的内存使用超过了usage
threshold,它将会把UsageThresholdExceeded
属性设置为true
有些内存池可能不支持usage threshold。你能够使用UsageThresholdSupported属性来推断一个内存池是否支持
usage
threshold。例 如。一个比較完好(generational garbage collector)的垃圾回收器(如HotSpot的虚拟机),most of the objects are allocated in the young generation,从eden内存池中产生。
eden pool被设计成能够被装满;再eden pool中运行垃圾回收将会释放他
Collection usage threshold是可进行垃圾回收的内存池的一个可配置属性。JVM堆一个内存池进行 垃圾回收以后,此内存池中的一些内存仍然被那些没有被回收的对象占用。collection usage threshold仅同意你在垃圾回收后对内存进行检查。假设JVM发现可用内存超出collection usage threshold,它将会设置CollectionUsageThresholdExceeded
属性为true。
你能够使用CollectionUsageThresholdSupported
属性来控制内存池释放支持 collection
usage threshold.
usage threshold 和collection usage threshold是MBean标签中的一组。比如,在左边的树形结构中选择TenuredGen,设置tenured generation memory pool的usage threshold为6m。
例如以下图所看到的
Figure 6: Setting Usage Threshold.
当 TenuredGen
memory pool的内存使用超过6MBytes时,代表 TenuredGen
memory
pool的柱状图将会呈现红色来代表使用的内存超过了usage threshold。代表堆内存的柱状图也将变为红色。你能够选择柱状图或者在图表中指定内存池来查看某个指定内存池的信息。假设把鼠标房子柱状图上。将会显示出内存池的名字
Figure 7: Low Memory.
开启/关闭虚拟机的具体跟踪
如上所述,内存系统的MBean定义了一个叫做Verbose布尔变量,让你能动态的打开或关闭具体的GC跟踪。具体的GC跟踪。将会在JVM启动时显示。默认的HotSpot的GC具体输出为stdout
.
Figure 8: Setting Verbose GC.
死锁检查
线程标签页提供关于应用的线程执行信息
Figure 9: Threads Tab.
左下角列出了所以正在执行的线程。假设你在过滤器中输入一个字符。线程列表将仅显示线程名字包括你输入字符的线程。通过点击某个线程,你能够获取这个线程的相关信息。
线程的MBean标签提供了一些Thread标签没有提供实用的操作。
findMonitorDeadlockedThreads
. 假设发生线程死锁,能够通过这个检查出来。操作返回一组死锁的线程IDgetThreadInfo
. 返回线程的信息。包含线程的名称、堆栈信息,导致当前线程堵塞的锁,假设有的话,还返回哪儿线程持有这个锁。和这个线程信息的统计。getThreadCpuTime
.返回指定线程消耗的CPU时间。
Figure 10: MBeans Tab Threading.
为检查你的应用是否进入死锁(比如,你的应用挂起),你能够使用findMonitorDeadlockedThreads
操作。
Figure 11: Find Deadlocked Threads.
一旦你选择了findMonitorDeadlockedThreads
button,将会有一个弹出窗体显示结果。在上面样例中,JConsole连接了一个存在3个死锁线程的演示样例应用SampleTest。
如上所看到的,检查出ID为12,10和11的线程死锁。想查询很多其它的线程信息,能够使用getThreadInfo
操作。线程的MBean支持getThreadInfo
操作的四种形式,
- 对一个给定的线程ID,给出最深的堆栈情况
- 堆一系列的线程ID,给出最深的堆栈情况
- Of a given thread ID with no stack trace.
- Of an array of thread IDs with no stack trace.
你能够在getThreadInfo操作的第一个參数中输入死锁的线程ID和你想跟踪的堆栈深度。
Figure 12: ThreadInfo for Thread ID = 12.
双击stackTrace属性的值域将会显示一个复合对话框。你能够在堆栈中来回查看。图13,14显示了死锁线程-1的复合对话框中的第一层堆栈和第二层堆栈。
Figure 13: Top Frame of the Stack Trace of DeadlockedThread-1.
Figure 14: Second Frame of the Stack Trace of DeadlockedThread-1.
线程标签页提供了一个友好的界面供查看线程的堆栈。你能够找到死锁线程的名字,使用getThreadInfo
查找线程信息。然后又能够使用线程标签页来分析死锁。
控制日志等级
Logging MBean定义了LoggerNames
属性,用于描写叙述日志名称。为找到你的应用的日志。能够选择在MBeans树中java.util.logging
下的Logging
MBean。双击LoggerNames属性
Figure 15: List of All Logger Names.
Logging MBean也支持三种操作:
getParentLoggerName
. 返回指定logger的父loggergetLoggerLevel
. 返回指定logger的日志等级setLoggerLevel
.设置指定logger到一个新的等级
Figure 16: Setting Log Level.
获取操作系统资源信息-Sun平台下的扩展
JDK5.0扩展了操作系统的MBean,以此能够获取一下系统资源的信息,如:
- 处理的CPU
- 总共的和空暇的物理内存
- 可获得的虚拟内存。
(即保证能够分配给执行的进程的虚拟内存)
- 总共的和空暇的交换区
- 打开的文件总数(仅仅能在Unix下使用)
你能够监控不论什么一个属性随时间的变化——如。CPU时间-双击属性的值域部分。
Figure 17: MBeans Tab OS.
除此之外,VM标签和Summary标签提供了操作系统资源的一些信息
管理应用的MBean
被监控的SampleTest应用有它自己的Hello MBean:
com.sun.example:type=Hello 假设CacheSize 属性发生改变,Hello MBean将会发送一个通知。 你能够和管理平台的MBeans一样使用MBeans标签页来管理你的应用的MBean。比如,当CacheSize 属性变化的时候你想监控。你首先能够在 Notification标签页中订阅。假设你改变CacheSize,你能够看到一个通知被发送。
Figure 18: Notifications.
相关信息
JProfiler试用手记
JProfiler是一款Java的性能监控工具。能够查看当前应用的 对象、对象引用、内存、CPU使用情况、线程、线程执行情况(堵塞、等待等),同一时候能够查找应用内存使用得热点。即:哪个对象占用的内存比較多;或者CPU热点。即:哪儿方法占用的较大得CPU资源。我使用的是4.3.2版本号,曾经试用过3**版本号,只是那个bug比較多。easy死。4**版本号稳定多了。有了上面那些信息对于系统的调优会有非常大帮助。这里提供有几篇文章供參考:获取、介绍,简单入门,使用JProfiler解决实际问题。这几篇文章基本介绍了常见东西了,以下说点心得。
- JProfiler监控是要消耗系统资源的,所以普通情况下不要用于性能測试时候的监控。
- 假设要用于相对大压力情况下,能够有选择的打开监控项。不用全部都打开。主要有两个,一个是内存监控,打开的情况下能够查找内存分配热点。一个是CPU监控,打开的情况下能够查看CPU使用热点。
- 个 人觉得最好用的(也是用的最多的)是查询当前的对象的数量。
数量监控非常重要,假设你使用了单例,那么你仅仅会看到有一个对象存在,假设多了就说明程序有问题 了。相同,假设应用进行一系列操作。检查一下该销毁的对象是否还继续存在,假设没有释放,就得考虑是否存在内存溢出了。
- JProfiler还提供了一个比較好的检查内存溢出得工具。他能够查找某个对象的引用情况,即:当你发现某个该释放掉的对象没有释放,就能够看一下哪个实例在引用它,找到了根即找到了溢出点。
详细操作例如以下:在 “Memory Views”界面中右键选择你要监控的对象。选择第一项“Take Heap Snapshot for Selection”,选择完毕后会进入“Heap Walker”界面,界面以下提供几个功能,选择“References”就可以 。如图: - JProfiler提供不同的观察粒度。提供对类的监控、对包的监控、对J2EE组件的监控,同一时候过滤器也比較好用,直接定位你关注的包或类就可以。
- JProfiler的监控可能与应用之间存在一定时间差。所以有些时候须要等待刷新,才干显示正确系统情况。