JVM运行状态监控及分析_第1页
JVM运行状态监控及分析_第2页
JVM运行状态监控及分析_第3页
JVM运行状态监控及分析_第4页
JVM运行状态监控及分析_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

JVM运行状态监控及分析TITLE技术方案技术方案简介目的背景应用系统运行时出现内存溢出问题,需要定位及解决。定义、首字母缩写词和缩略语暂无。参考资料本方案的参考资料如下:周志明,《深入理解Java虚拟机:JVM高级特性与最佳实践》MAT,/mat从转储(Dump)文件中调试并除错,/question/129540%5f23220……约束本文描述的工具,只在JDK6以上版本中进行验证。可能部分工具在JDK1.4.2_14和JDK1.5.0_06以上也能够执行,但并未实际测试验证,仅供参考。常见JVM内存相关问题现象通常内存溢出时JVM会提示具体的内存溢出原因,下面是几种常见的情况及简要的原因说明及相关的JVM配置。栈溢出:StackOverflowErrorJVM输出信息:“java.lang.StackOverflowError”。JVM相关机制:JVM在执行Java方法调用时需要使用栈传递调用参数、返回值以及保存局部变量表,通常组织为栈帧(StackFrame)结构。从概念上说,每次Java方法调用都会消耗一定的栈内存,当这个方法调用结束返回后释放这部分内存。而每个Java线程对应的栈内存是有限的(通常JVM启动后就固定了),因此当方法嵌套层数过多或者栈帧内的数据结构(如局部变量表)过大时,可能出现StackOverflowError。JVM控制参数:可以通过-Xss1024K设置线程栈大小为1024K。通常无需设置此参数,在不同OS下的JVM均有自己的默认值设置,一般在256K-1024K之间。常见原因分析建议:出现此问题通常应该先分析应用系统的原因,而不是考虑增大“-Xss”参数设置值。因为出现此问题的最可能原因是过深的方法调用(比如错误的递归调用造成方法调用层次过深),即应用程序中的编程错误造成栈溢出。当出现StackOverflowError时,根据此异常的详细信息通常可以比较明确地找到错误代码的可能位置,通常不需要复杂的工具支持。堆溢出JVM输出信息:“java.lang.OutOfMemoryError:Javaheapspace”,或者类似输出信息,视JVM厂商及版本略有区别,但关键字都是“heap”或者“堆”。JVM相关机制:从概念上说,Heap是存储Java类实例和数组的内存区。比如任何“obj=newXClass();”的调用都是在Heap中进行内存分配,当不能成功进行这种内存分配时,JVM将抛出“java.lang.OutOfMemoryError:Javaheapspace”。JVM控制参数:-Xms512m:设置Heap最小值为512m;-Xmx1024m:设置Heap最大值为1024m;JVM控制参数设置相关知识及建议:在生产环境中可以将-Xms和-Xmx设为相同大小,避免JVM动态调整Heap大小;在32位系统上(无论是Windows还是Unix/Linux),-Xmx参数通常无法设置到大于1500m,且此值在不同的系统上并不相同;在32位系统上,并不建议将-Xmx设到最大(如1500m)。因为32位系统中JVM进程的可用的内存是有限的(一个32为系统上的进程的地址空间最大为4G,能使用的内存最大为4G,实际上OS核心会占据1~2G地址空间,而留给应用层的地址空间通常不超过3G,应用层真正能够使用的通常在2G以内),这些内存除了JavaHeap使用,JVM进程本身、NativeThread都需要使用。因此过大的JavaHeap可能增大其他溢出的可能;常见原因分析建议:Java堆内存溢出是较为常见的情况,有可能是因为:应用系统的错误导致的问题,即应用系统错误地生成了太多的新Object,JVM中用来存储对象的堆(heap)中无法容纳新Object。此时需要通过分析HeapDump(堆转储文件)定位并修正问题;也有可能并非程序错误,而是业务场景或者并发程度确实需要更大的Heap来支持。此时有多种可能的解决方案:分析HeapDump(堆转储文件),确定应用中内存使用情况,更改应用的实现逻辑,减少新Object的创建。其本质是以时间换空间的策略,即增加应用逻辑执行时间,来换取更少的内存需求;增大Heap,即设置更大的-Xmx参数。32位系统上余地并不大,并可能增大Thread相关内存溢出的可能性;更换为使用64位系统,64位系统才能支持更大的Heap,但增大Heap带来的不仅是好处,也可能造成GC时间增加,应用系统“停顿”的感觉更明显;堆内存溢出较为常见,准确地定位及分析需要借助工具,后文将详细描述如何借助工具进行分析。持久区(方法区)溢出JVM输出信息:“java.lang.OutOfMemoryError:PermGenspace”。JVM相关机制:概念上说,JVM持久区(方法区)是用来存储类型相关的信息,如该类型的常量池,字段或方法信息,而且类型中的类(静态)变量同样也是存储在方法区中(如到ClassLoader的引用和到Class类的引用)。当持久区不足以容纳需要加载的Class时JVM将抛出此异常,异常中明确指出是持久区内存不足。JVM控制参数:-XX:PermSize=64m:设置初始持久区大小为64m;-XX:MaxPermSize=256m:设置持久区最大为256m;常见原因分析建议:此问题通常是太多的Class需要加载造成的,Class的数量和应用系统的代码规模相关,较大的应用系统需要较大的MaxPermSize设置。需要注意的是,JavaEE应用中的jsp最终会被编译成Class并加载,因此可能在大量使用jsp的JavaEE应用系统中需要设置较大的MaxPermSize,但通常不应该超过256m。另外,对于使用了ASM/CGLib等字节码工具动态生成Class的系统,编程错误导致的运行时大量生成Class也可能导致此异常。此异常明确地说明了问题,且除了编程错误一般都能够通过加大MaxPermSize参数值来解决,通常不需要复杂的工具进行分析定位。无法创建OS本地线程JVM输出信息:“java.lang.OutOfMemoryError:unabletocreatenewnativethread”。JVM相关机制:概念上说,JVM中启动一个Thread,通常会在OS中创建并启动一个本地线程(NativeThread),而无论是因为内存问题还是本地线程数量问题,都有可能导致此异常。NativeThread内存问题:OS在创建本地线程时同样需要使用内存,但这个内存不在JVM的堆内存范围内,而是在JVM进程之内、JavaHeap/方法区等内存之外。因此完全有可能JavaHeap越大,剩余的用来创建NativeThread的内存越小,从而可创建的NativeThread越少,出现此异常的可能性越大;NativeThread数量问题:OS对于进程允许创建的线程数量通常有限制,因此JVM能够启动的Thread数量也有限,当JVM中启动的Thread超出此限制时将出现此异常。通常JVM中已经启动的Thread数量应该是有限的,除非是程序错误启动了大量的Thread。JVM控制参数:无,但通常减少-Xmx的值能够保留更多的内存给OS线程。常见原因分析建议:出现此问题时,只要获取JVM当时的ThreadDump(线程转储信息),并对ThreadDump进行分析即可。分析ThreadDump信息很容易获取JVM线程相关的信息,判断JVM中是否存在预料之外的线程,或者线程所处的状态和正在执行的调用是否符合预期。内存泄漏严格意义上来说,我们一般遇到的JVM中的内存泄漏其实不是内存泄漏,因为严格地说内存泄漏指的是已经分配的内存无法被应用系统正确回收。而我们通常说的JVM中的内存泄漏只是部分对象被其他对象持有引用,因此不能被GC过程回收;同时,这些不能回收的对象被应用系统“遗忘”了,不再有任何用处的同时还占据着大量内存。在应用系统看来这部分内存像是丢失了(或者泄露了)。我们解决内存泄漏问题就是要找到的是这些应用程序中的错误。举个简单的例子:MapglobalMap=newHashMap<String,List<SomeObject>>();globalMap在整个应用系统生命周期内都会被引用,因此不能被GC回收。因而任何globalMap.put(aString,aList)的操作将在globalMap中引用aString和aList两个对象,直到被明确清除出globalMap后aString和aList才有可能被GC回收,否则此两个对象将永远占据JVM内存。如果应用系统是“忘了”从globalMap中remove(aString),则对应用系统来说就像是aString和aList所占的内存“泄漏”了。JVM异常退出JVM运行过程中有可能异常退出,即JVM进程忽然消失,此时通常不是JavaStack或者JavaHeap出现异常,而是与OS本地堆栈有关。JVM中运行的应用系统通常难以处理此问题,但如果应用系统中使用了JNI,则有可能与此部分代码相关。在Windows上,JVM异常退出时通常会生成“hs_err_pidXXXX.log”文件,此文件位于JVM进程的“当前路径”下(如Tomcat的bin/),可以从此文件中得到异常退出时的现象。遗憾的是,应用系统开发人员很难分析此文件,但可以根据此文件得到一些信息,或者将此文件反馈给能够分析的技术社区。下面列举几种HotSpotJVM异常退出时产生的“hs_err_pidXXXX.log”文件中的信息:#AnunexpectederrorhasbeendetectedbyJavaRuntimeEnvironment:#EXCEPTION#AnunexpectederrorhasbeendetectedbyJavaRuntimeEnvironment:#EXCEPTION_STACK_OVERFLOW(0xc00000fd)atpc=0x6da3a7fd,pid=3012,tid=3108#JavaVM:JavaHotSpot(TM)ClientVM(11.0-b15mixedmodewindows-x86)#Problematicframe:#V[jvm.dll+0x18a7fd]#Ifyouwouldliketosubmitabugreport,pleasevisit:#/webapps/bugreport/crash.jsp##AnunexpectederrorhasbeendetectedbyJavaRuntimeEnvironment:#AnunexpectederrorhasbeendetectedbyJavaRuntimeEnvironment:#java.lang.OutOfMemoryError:requested1024000bytesforGrETinC:\BUILD_AREA\jdk6_10\hotspot\src\share\vm\utilities\growableArray.cpp.Outofswapspace?#InternalError(allocation.inline.hpp:42),pid=4060,tid=3816#Error:GrETinC:\BUILD_AREA\jdk6_10\hotspot\src\share\vm\utilities\growableArray.cpp#JavaVM:JavaHotSpot(TM)ClientVM(11.0-b15mixedmodewindows-x86)#Ifyouwouldliketosubmitabugreport,pleasevisit:#/webapps/bugreport/crash.jsp注:上面列举的JVM异常只包含部分总结性的信息,log文件中还有更多的详细信息。HotSpotJVM之外的其他JVM是否有类似信息尚未确定,但此类问题通常应用层开发者难以解决(JNI相关应用的开发者除外)。JVM运行时信息收集ThreadDump/线程转储ThreadDump中包含下列信息:所有JVM中启动了但是未结束的Thread列表;每个Thread当前所处的状态及其调用栈(StackTrace);JVM内部是否出现死锁(Deadlock);基于ThreadDump信息可以确认JVM当前的执行状态,ThreadDump信息常见的收集方法包括:使用jvisualvm:在所有平台上,当可以通过jvisualvm监控JVM运行时,可以直接在jvisualvm中进行ThreadDump,请参见jvisualvm的说明;Windows平台:在JVMConsole(比如Tomcat启动时的后台窗口)下按下组合键Ctrl-Break,Console中将打印ThreadDump信息。当Console的缓冲区不够大时可能无法显示全部信息,需要增大Console的缓冲区;Unix/Linux平台:先找到对应的JVM进程号(pid):ps–ef|grepjavakill-3(JVM进程号):此时ThreadDump信息将输出到JVMConsole中。此命令不会终止JVM运行,只是输出ThreadDump信息;JavaHeapDump/堆转储JVMHeapDump的方法与JVM密切相关,因此后面的描述基于不同运行环境的JVM分别描述。SUN/OracleHotSpotJava6+版本的SUN/OracleHotSpotJVM支持下列方式生成HeapDump:基于JVM事件:当JVM运行过程中出现OutOfMemoryError时自动HeapDump,这也是生产系统中最可行的HeapDump生成方法。涉及参数包括:-XX:+HeapDumpOnOutOfMemoryError:当内存溢出时进行HeapDump,默认在Java进程的“当前目录”下生成类似于“java_pid1340.hprof”形式的文件。可以用-XX:HeapDumpPath控制生成文件的位置;-XX:HeapDumpPath=path:设置OutOfMemoryError时JVM生成HeapDump文件的位置。此参数默认值为“./java_pid%p.hprof”,其中“%p”代表Java进程的PID(ProcessID,进程标识),所以会在Java进程的当前目录下生成包含PID的.hprof文件;交互式HeapDump:在JVM运行中,以交互式方式获取HeapDump,通常用于开发调试阶段定位问题,生产环境还是建议基于JVM事件进行HeapDump,方式包括:使用jvisualvm:在所有平台上,当可以通过jvisualvm监控JVM运行时,可以直接在jvisualvm中进行HeapDump,请参见jvisualvm的说明;使用JDK中的jmap工具:jmap-dump[live,]format=b,file=filenamepid使用OS功能:在Linux中,使用“无害”的gcore命令或破坏性的“kill-6”或“kill-11”命令来生成一个内核文件。然后,使用jmap从内核文件中提取一个堆转储文件:jmap-dump:format=b,file=heap.hprofpath_to_java_executable_core;使用Ctrl+Break:如果运行的应用程序设置了-XX:+HeapDumpOnCtrlBreak命令行选项,那么在通过控制台发出Ctrl+Break事件或SIGQUIT(通常通过kill-3生成),那么会生成一个HPROF格式的转储文件和一个线程Dump。有一些版本不支持这个选项,那么在遇到这些情况时可以尝试使用:-Xrunhprof:format=b,file=heapdump.hprof。并不推荐使用这种方式,一些HotSpot版本不支持此参数,建议使用上述其他交互式方式。IBMAIX+J9JVM此环境下可以通过下列方式获取HeapDump:基于JVM事件:当JVM运行过程中出现OutOfMemoryError时自动HeapDump,这也是生产系统中最可行的HeapDump生成方法。涉及参数包括:设置JVM启动时的环境变量:exportIBM_HEAP_DUMP=trueexportIBM_HEAPDUMP=trueexportIBM_HEAPDUMP_OUTOFMEMORY=trueexportIBM_JAVADUMP_OUTOFMEMORY=trueexportIBM_JAVACORE_OUTOFMEMORY=true设置JVM启动参数:-XX:+HeapDumpOnOutOfMemoryError-Xdump:java+system:events=systhrow,filter=java/lang/OutOfMemoryError,range=1..4,request=exclusive+compact+prepwalk:设置在前4个OutOfMemoryError时进行HeapDump;基于OS和JDK命令:通过OS命令gencore生成进程COREDUMP,然后通过JDK命令工具jextract获取HeapDump;此方法较为复杂,并不建议用于生产环境;建议基于JVM事件获取HeapDump。GC信息GC参数在不同JVM的或不同JVM版本中均有不同,但一般能够支持下列参数,系统运行时可以设置GC日志文件以收集GC信息供后续分析:-Xloggc:gc.log:指定将gc的信息输出到gc.log中;-verbose:gc:输出详尽的GC信息,通常verbose都只能用于开发调试环境,而不应该用于生产环境;-XX:+PrintGC:输出GC的简要信息;-XX:+PrintGCDetails:GC的详细信息;-XX:+PrintGCTimeStamps:GC的时间信息;-XX:+PrintGCApplicationStoppedTime:GC造成的应用暂停的时间;-XX:+PrintTenuringDistribution:GC信息可以提供简要的GC运行过程信息,但不太容易基于GC信息判断JVM有什么异常状态,因此GC信息收集方法仅供参考。JVM远程监测jvisualvm只能远程连接启用了JMX远程监测的JVM,JVM启动时需要设置一些系统属性来启用JMX远程监测。JVM中JMX相关的配置文件及说明位于固定位置,一般位于“JDK/jre/lib/management”,其中有JMX配置的模板文件。因为一般JDK在服务器上是公用的,因此不应该直接在此位置更改JMX配置,而应该复制其中的文件后更改,并在JVM的启动参数中指定使用更改后的文件。在JVM启动参数中指定JMX配置文件的方式为:-Dcom.sun.management.config.file=<somewhere>.perties,JMX的配置信息在此文件中设置,下面描述JMX配置文件中的典型配置。无认证方式启用JMX并且不进行认证,此时任何JMX客户端都可以远程连接到此JVM进行远程监测,因此安全性很差,但在开发调试和测试环境中较为方便。在JVM启动参数中设置“-Dcom.sun.management.config.file=C:\JMXRemote\perties”,同时在JMX配置文件(C:\JMXRemote\perties)中设置下面的值:#设置JMX端口号,JMX客户端连接时使用此端口com.sun.management.jmxremote.port=9999#设置JMX不使用SSL连接com.sun.management.jmxremote.ssl=false#设置JMX客户端连接本JVM时无需身份认证com.sun.management.jmxremote.authenticate=false上述设置后启动JVM,则任何JMX客户端均可以连接此JVM并进行JVM运行状态监测。认证方式启用JMX且需要身份认证,只有认证通过的JMX客户端才能够连接到此JVM进行监测。在JVM启动参数中设置“-Dcom.sun.management.config.file=C:\TEMP\JMXRemote\auth\perties”,并在此文件中设置下列值:#设置JMX端口号,JMX客户端连接时使用此端口com.sun.management.jmxremote.port=9999#设置JMX不使用SSL连接com.sun.management.jmxremote.ssl=false#设置JMX客户端连接本JVM时需要身份认证com.sun.management.jmxremote.authenticate=true#设置身份认证文件地址com.sun.management.jmxremote.password.file=C:/TEMP/JMXRemote/auth/jmxremote.password#设置角色文件地址com.sun.management.jmxremote.access.file=C:/TEMP/JMXRemote/auth/jmxremote.access注意:access和password文件必须正确设置才能生效,建议只改动password文件中的口令以避免其他错误。而且jmxremote.password文件必须正确设定其在文件系统中的权限,否则JVM无法启动,通常需要设置为此文件只有所有者能够访问。如果需要设置JMXServer的监听地址,可以设置下列参数:-Djava.rmi.server.hostname=x.x.x.x"上述设置后启动JVM,则任何JMX客户端均可以连接此JVM并进行JVM运行状态监测。常用工具简述Java相关的常用工具及参考位置。jvisualvmjvisualvm是SUN/OracleJDK自带的JVM运行状态监测工具,能够获取JVM运行状态的各种信息,包括ThreadDump和HeapDump,在可以使用的情况下建议使用此工具监测JVM运行状态。连接远程JVM当设置了JMX远程监测后启动JVM(如启动Tomcat)后,可以在其他机器上用jvisualvm远程监测此JVM(如Tomcat),过程如下:启动jvisualvm,增加远程主机(即配置了JMX的JVM所在的主机),如下图:输入远程主机地址,如下图:确定后,增加JMX连接,如下图:输入JMX连接信息,JMX端口信息即是com.sun.management.jmxremote.port=9999中设定的端口号,如下图:确定后,如果JMX配置为无认证需求,则连接完成;否则将要求输入JMX连接用户名和口令,即在文件com.sun.management.jmxremote.password.file=<password_file>中设置的用户名和口令。如下图:用户名和口令验证成功后连接完成,如下图:获取ThreadDump直接在jvisualvm中获取ThreadDump,如下图:ThreadDump的结果如下图:在其中进行ThreadDump分析即可。获取HeapDump直接在jvisualvm中获取HeapDump,如下图:执行HeapDump后将提示HeapDump创建位置,如下图:确定后将创建HeapDump文件,之后在MAT中分析此文件即可。EclipseMemoryAnalyzerTool,MATMAT是JavaHeapDump文件的分析工具,在出现OutOfMemoryError的情况下,通过MAT可以帮助准确定位内存溢出原因。IDMAIX环境中生成的COREDUMP文件不是MAT默认识别的.hprof文件格式,需要在MAT中安装IBMDTFJ插件才能够打开并分析IBMDUMP文件。由于完整的JavaHEAPDUMP文件通常较大(1G以上),因此MAT通常在64位机器和JDK上才足够进行分析,因此应该使用64位MAT并在MemoryAnalyzer.ini配置文件中设置足够大的-Xmx参数。后文的内存溢出场景分析全部基于MAT。MAT本身的帮助信息简短扼要,下面简单介绍几个常用的功能。打开HeapDump及LeakSuspectsReportsMAT中打开HeapDump文件后,将提示是否进行内存泄漏分析并产生报告,一般这个分析都能发现可能的泄漏点,建议进行分析,如下图:分析结果如下图,其中描述了可能的泄漏点:如果报告中提示“ThestacktraceofthisThreadisavailable.Seestacktrace.”,则说明疑似泄漏点有ThreadStack信息,这是最理想的情况,几乎可以立刻发现可能的内存泄漏点代码位置,如下图:上面红线位置的jsp应该就是泄漏点。但HeapDump中不一定都包含ThreadStack信息,比如IBM的.phd格式的HeapDump中就不包含ThreadStack。通常JDK6+HotSpotJVM的HeapDump(.hprof)和IBMJ9生成的COREDUMP文件中会包含ThreadStack,因此建议获取最完整的HeapDump文件进行分析。查看Heap中的对象Heap中的对象是HeapDump中的主要内容,可以查看HeapDump中的全部对象,如下图:查看结果如下图:可以在其中查询任意Class,如下图:可以查看某个类型的对象集合,如下图:实例信息如下图:之后可以查看每个对象的内容,并判断是否是应该存在的对象。查看DominatorTree简单地说,DominatorTree是对象引用关系图的子集,通过DominatorTree可以很清晰地发现“大对象”,即从此“大对象”开始占用大量内存的对象集合。查看DominatorTree,如下图:DominatorTree数据如下图,可以清楚地看见占用内存最大的对象子集:之后分析这个对象包含的其他对象关系,如下图:通常DominatorTree有助于找到这些占用大量内存的对象子集并进行分析。查看线程信息如果ThreadDump中线程信息可用,可以直接查看线程相关信息,如下图:线程相关信息如下图:图中红线标识的部分是线程对象及其引用其他对象共占用的内存,可以看出此值过大,可以对此线程对象进行分析以找到占用这些内存的原因。场景分析示例下面列举一些场景并分析其可能存在的问题。JVM内的Deadlock与ThreadDumpJVM可以检测到synchronized导致的死锁,考虑下面的代码:publicclassClassA{ publicclassClassA{ publicstaticStringmonitor1="monitor1"; publicstaticStringmonitor2="monitor2"; publicstaticvoidmain(String[]args){ Threadthread1=newThread(newRunnable(){ publicvoidrun(){ try{ synchronized(monitor1){ System.out.println(Thread.currentThread().getName()+":gotmonitor1"); Thread.sleep(5000); synchronized(monitor2){ System.out.println(Thread.currentThread().getName()+":gotmonitor2"); }

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论