分类: Java
2012-09-17 14:01:54
JAVA开发我们常常会遇到内存分配和性能调优方面的要求,在性能调优方面没有固定的最优方案,这就需要我们去根据实际情况分析,然后给出最适合的方案。因此利用JConsole可以很方便进行观察。
一、被控端配置
要运行JConsole,需要首先被监控端要进行相应的配置,我们要在被控制端启动JAVA程序时补充上如下代码:
-Dcom.sun.management.jmxremote //这里指定启动JMX
-Dcom.sun.management.jmxremote.port=8061 //这里是指定端口号
-Dcom.sun.management.jmxremote.ssl=false //这里指定是否使用SSL进行通讯加密
-Dcom.sun.management.jmxremote.authenticate=false //这里指定是否使用证书
如果我们需要启用远程用户身份认证(用户名和密码),则可以加入下面的参数:
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=$JRE_HOME/lib/management/password.properties //这里指定你放置的password.properties 文件的路径
-Dcom.sun.management.jmxremote.access.file=$JRE_HOME/lib/management/access.properties 这里指定你放置的access.properties文件的路径
这里的password.properties 和access.properties文件可以从JRE_HOME/lib/management/目录下去复制jmxremote.password.template文件到指定目录下,并将其改名为password.properties 。在这个文件中可以指定用户名和口令,此文件可以用记事本打开。指定格式是“user password”,前面是用户名,然后跟空格,然后是此用户密码,如果有多个用户,则换行输入按此格式输入第二个用户信息,以此类推。
从JRE_HOME/lib/management/目录下去复制jmxremote.access文件到指定目录下,在这个文件中可以指定用户名的权限,此文件可以用记事本打开。指定格式是“user 权限”,前面是用户名,然后跟空格,然后是此用户权限,如果有多个用户,则换行输入按此格式输入第二个用户信息,以此类推。
配置好被监控端后,就可以-Dcom.sun.management.jmxremote.authenticate设置为false,则以上配置不生效。
配置好被监控端后,我们可以像正常情况一样启动应用程序,这时我们就可以在远端通过JConsole监视此应用程序的运行情况了。
二、使用JConsole
在Command中运行JConsole(我只介绍通过UI方式进行连接,通过命令方式连接我就不介绍了,其实很简单)。JConsole有两种连接监控模式:Local Monitoring和Remote Monitoring。
连接远程被控端时,我们使用Remote Monitoring,在相应的设置栏中输入远程计算机的ip和端口号即可。
1、查看概述
在这里可以看到堆内存使用情况、活动线程数量、类的装入情况、CPU使用情况的一个全览图。
2、查看内存
这个Tab页上提供内存消耗和内存池的情况信息。界面上的图表反应了JVM的内存使用相对时间的变化情况,这里包括像堆(heap)、非堆(non-heap)和一些特定内存池的使用情况。HotSpot的内存池包括:
Eden Space (heap):这个内存池在对象初始化时被分配;
Survivor Space (heap):这个内存池中包含着Eden Space 经过GC之后幸存下来的对象;
Tenured Generation (heap):这个内存池包含着在survivor space中存在了很长一段时间的对象;
Permanent Generation (non-heap):这个内存池中包容着虚拟机自己反射的数据,例如像类和对象的方法。JVM采用类数据共享来将数据分割在只读和读写两个区域中。
Code Cache (non-heap):HotSpot JVM 总是包含一个代码缓存,在这个缓存区中包含着被编辑和存储的本地代码。
在界面上详细信息区域列出了当前已使用内存大小、已分配内存大小、最大允许分配内存值是多少等信息。
当前已使用内存大小反映了内存已被使用情况,它包括被所有对象(包括可到达对象和不可到达对象)占据的内存值。
已分配内存大小这个值反映了可被JVM使用的内存总数。已被分配内存大小可以随时间而变化,JAVA虚拟机可以释放内存给系统,并在开始初始化时分配很少的内存。已被分配的内存大小一定大于或等于已被使用的内存大小。
最大允许分配内存值反映可以被内存管理器使用的最大内存数。这个值可以未定义也可以进行改变。如果JVM试图增加被已被使用内存的大小大于已分配内存大小,则内存分配将失败。通常已被使用内存大小要小于或者等于最多允许分配内存值。
右下脚的Bar图表显示了堆和非堆对内存池的占用情况,当被使用的内存超出可以使用内存的起点数,此Bar图标上将显示红色。通过MemoryMXBean的属性可以设置可用内存的起点数。
3、堆内存和非堆内存
JVM管理着两类内存:堆内存和非堆内存,JVM开始运行就会自动创建它们。
堆内存是JVM运行时数据区域,它为类实例和数组分配的内存。堆可以是固定大小的也可以是可变大小的。garbage collector 是一个自动内存管理系统,它回收为对象分配的堆内存。
非堆内存包括所有线程之间共享的一个方法区域和JVM为优化或内部处理所分配的内存。它存储每一个类的结构,如一个运行时的常量池、字段和方法数据、方法的代码和构造函数。这个方法区是逻辑上堆的一部分,但依赖于实现,一个JVM可以不去回收或者压缩它。像堆一样,方法区可以固定大小的,也可以是大小可变的。方法区不是必须是连续的,它们可以是不连续的。
除方法区之外,JVM总是从非堆中分配用于优化和内部处理所需的内存。例如,JIT编译器为高性能的JVM代码转换存储成本地代码而分配的内存。
4、内存池和内存管理器
内存池和内存管理器是JVM内存系统关键的方面。
一个内存池代表JVM管理的一个内存区域。JVM至少有一个内存池,在执行期间它可以去创建或者移除一些内存池。一个内存池可以隶属于堆内存或非堆内存中的任意一个。
一个内存管理器管理着一个或者多个内存池。garbage collector 是内存管理器中重要的一种,它回收被无法达到对象使用的内存。JVM可以有一个或者多个内存管理器,它在运行期间可以增加或者移去一些内存管理器。一个内存池可以被一个或者多个内存管理器管理。
5、Garbage Collection
Garbage Collection简称GC,GC用来释放没有被引用的对象所占领的内存。这意味着对象如果存在有效引用其就是“alive”(活着的),如果对象没有被引用(或不可到达)则我们认为它是“dead”(死亡的)。Garbage collection 是用来释放被死亡的对象所占用的内存的进程。GC通过算法和参数的配置可以对性能产生效果显著的影响。
HotSpot虚拟机垃圾回收器使用通用的Garbage Collection。使用通用GC的显而易见的优势在于(事实上对于绝大多少程序是这样):
所以,通用GC会将内存分配成少许Generation(代),以及与此关联着的内存池。当通用内存消耗完被分配的内存时,虚拟机会在内存池上执行一个局部的GC(总是调用minor collection)去释放被dead的对象所占用的内存。这个局部的GC通常比完全GC要快许多。
HotSpot 虚拟机定义两个Generation:Young Generation (有时候也称为Nursery)和Old Generation。Young Generation由一个Eden Space和两个Survivor Spaces组成,虚拟机初始是分配所有的对象到Eden Space,许多对象也是在这里死去。当它执行一个minor GC的时候,虚拟机将从Eden Space中移动一些残余的对象到其中的一个Survivor Spaces中。虚拟机将在Survivor Spaces中生存足够长时间的对象移动到Old Generation的Tenured Spaces中。当Tenured Generation被填满,则将执行一个完全GC,这个完全GC非常的慢,因为它要处理所有存活着的对象。Permanent Generation 控制着所有虚拟机自己映射的数据,如类和对象的方法。
默认代( Generation )的排列如下图所示:
如果GC成为一个性能的瓶颈,我们可以通过配置代(Generation)的大小来优化和改善性能。具体如何配置我将安排在《配置Garbage Collection》文章中加以介绍。
6、监控线程的使用
通过观察线程Tab中内容可以了解线程的相关信息。
在界面左下角列出了全部活动的线程,在最下面的输入框中输入一些字符串后,系统会自动过滤以输入字符开头的线程列表。点击“检测到死锁”可以列出当前处于死锁状态的线程。点击列表中的线程名可以列出此线程的一些信息,如线程当前状态和线程的一些堆栈信息。
7、监控类的加载
通过监控类的加载可以观察到被监控端类的加载情况。
8、监控和管理MBeans
9、查看虚拟机信息