此篇文章用于查内存泄漏和FullGC
因为在Linux环境,用jdk自带的jmap工具(Linux/Unix环境特有的),可以对进程中的内存对象监视,然后就运行命令
jmap -histo [pid],找内存中的对象数目变化。
watch "jmap -histo 7220| grep u9"
查看有u9字段的对象数目和大小
查看
jvm中对象内存占用情况(jmap -histo pid | head -n8, 因为内容太多, 因此需要head一下)
导出整个JVM中的内存信息
通过以上方法能查看到JVM中对象内存的占用情况,但很多时候还要知道这个对象到底是谁创建的。例如上面显示出来的[C,只知道它占用了那么多的空间,但不知道是什么对象创建出的[C,于是jmap提供了导出整个jvm中的内存信息的支持。基于一些jvm内存的分析工具,例如sun JDK 6中的jhat、Eclipse Memory Analyzer,可以分析jvm中内存的详细信息,例如[C是哪些对象创建的。
----------------------------------------------------
执行如下命令即可导出整个jvm中的内存信息:
- jmap -dump:format=b,file=文件名 [pid]
- 在用jhat -J-Xmx1024M [file] 这里的file就是步骤1中的文件名,
- 执行后等待console中输出Started HTTP server on port 7000,看到后就可以通过浏览器访问http://ip:7000/了,此页面默认为按package分类显示系统中所有的对象实例。在页面的最下端有Other Queries导航,其中有显示jvm中对象实例个数的链接、有显示jvm中对象大小的链接等,点击显示jvm中对象大小的链接
-------------------------------------------------------
jmap -heap [pid] 在linux上执行改命令,就可查看整个JVM中各个代的内存状况
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 1610612736 (1536.0MB)
NewSize = 524288000 (500.0MB)
MaxNewSize = 524288000 (500.0MB)
OldSize = 4194304 (4.0MB)
NewRatio = 8
SurvivorRatio = 8
PermSize = 100663296 (96.0MB)
MaxPermSize = 268435456 (256.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 430702592 (410.75MB)
used = 324439936 (309.4100341796875MB)
free = 106262656 (101.3399658203125MB)
75.32806675098904% used
From Space:
capacity = 46333952 (44.1875MB)
used = 13016424 (12.413429260253906MB)
free = 33317528 (31.774070739746094MB)
28.092626331550566% used
To Space:
capacity = 46792704 (44.625MB)
used = 0 (0.0MB)
free = 46792704 (44.625MB)
0.0% used
PS Old Generation
capacity = 1086324736 (1036.0MB)
used = 945707880 (901.8973159790039MB)
free = 140616856 (134.1026840209961MB)
87.05572548059884% used
PS Perm Generation
capacity = 100663296 (96.0MB)
used = 46349592 (44.202415466308594MB)
free = 54313704 (51.797584533691406MB)
46.044182777404785% used
如果PS Old Generation或者PS Perm Generation被填满,就会导致fullGC,进而导致CPU占用率大幅度提高
--------------------------------
并发回收 CMS收集器 UseConcMarkSweepGC
适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
并行回收 UseParallelGC
--适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。
--缺点:垃圾收集过程中应用响应时间可能加长
最大垃圾回收暂停:指定垃圾回收时的最长暂停时间,通过-XX:MaxGCPauseMillis=<N>指定。<N>为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。设定此值可能会减少应用的吞吐量。
吞吐量:吞吐量为垃圾回收时间与非垃圾回收时间的比值,通过-XX:GCTimeRatio=<N>来设定,公式为1/(1+N)。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99,即1%的时间用于垃圾回收。
引用一个网友实例:
猜想:在HeapSpace逼近临界值的时候,分配小块内存(譬如创建一个对象,如果是分配大块内存,实际上JVM直接就报OutOfMemory了),JVM发现内存不足,会尝试去FullGC出空闲内存,每次还真能挤出一点点,但很快又得重新FullGC,我们知道,实际上FullGC是非常消耗CPU的操作。但为啥不报OutOfMemory呢?因为每次总能够释放出一点点内存,因此竟然就这么维持下去了。这里非常郁闷,JVM直接报OutOfMemory还好过一点,至少直接知道原因。
JVM一般启动参数有一个GCTimeRatio参数,表示目标吞吐量,JVM会自动调整各内存区大小去达到该吞吐量值,如果吞吐量低于某个值,是会自动OutOfMemory,目前该值默认是1,表示GC时间占总时间不超过1%,但目前来看,要通过这种方式去让JVM直接报告OutOfMemory而不是持续维持高CPU看起来是不行的。
已经可以定位到是内存达到临界值的问题,接下来问题就简单了,dump出HeapSpace空间,使用Eclipse Memory Analyzer分析一下,原因一目了然,有点讶异,竟然是BlackStar的detail工作模式导致的,这里是因为使用的时候策略没有调整好,调整detail策略,重新启动JVM,问题解决,当然这里BlackStar的detail工作模式的自我保护方式也不好,实际上当发现使用内存过多的时候,应该调整自动放弃。
----------------------------------------------------------------
JStat用于GC分析的参数有:-gc、-gccapacity、-gccause、-gcnew、-gcnewcapacity、-gcold、-gcoldcapacity、-gcpermcapacity、-gcutil。常用的为-gcutil。通过-gcutil可按一定频率查看jvm中各代的空间的占用情况、minor GC的次数、消耗的时间、full GC的次数及消耗的时间的统计,执行jstat -gcutil [pid] [interval],另外可以指定更新频率,如-h5 1s:每一秒更新一次,并每隔5条加上header显示,比如jstat -gcutil -h5 28525 1s,表示每隔一秒更新一次,并每隔5条加上header显示
可看到类似如下的输出信息:
S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 74.24 96.73 73.43 46.05 17808 382.335 208 315.197 697.533 45.37 0.00 28.12 74.97 46.05 17809 382.370 208 315.197 697.568
其中S0、S1就是Survivor空间的使用率,E表示Eden空间的使用率,O表示旧生代空间的使用率,P表示持久代的使用率,YGC表示minor GC的执行次数,YGCT表示minor GC执行消耗的时间,FGC表示Full GC的执行次数,FGCT表示Full GC执行消耗的时间,GCT表示Minor GC+Full GC执行消耗的时间。
观察了一下外网数据,每隔两分钟会执行一次minor GC,也就是说Eden空间每隔两分钟就会被充满,然后会填充S0或者S1,填充次序为轮换执行,也就是这次为S0,下次必为S1,
FGC的平均时间为1S左右,minor GC的平均时间为16ms,也就是一个晶振时间,平均一天一次FGC,710次minor GC
对TJ06-盛大3区 gameserver采样
S0:Heap上的 Survivor space 0 段已使用空间的百分比
S1:Heap上的 Survivor space 1 段已使用空间的百分比
E: Heap上的 Eden space 段已使用空间的百分比
O: Heap上的 Old space 段已使用空间的百分比
P: Perm space 已使用空间的百分比
YGC:从程序启动到采样时发生Young GC的次数
YGCT:Young GC所用的时间(单位秒)
FGC:从程序启动到采样时发生Full GC的次数
FGCT:Full GC所用的时间(单位秒)
GCT:用于垃圾回收的总时间(单位秒)
jstat -gccapacity <jpid>:JVM各区的剩余状态
------------------------------------------------------
jps 目前正在运行中的java进程
-----------------------------------------------------
jstack
jstack -l <jpid>:显示线程阻塞/死锁情况
了解 Java 进程及其对应的执行线程内部发生的情况是一种常见的诊断挑战。例如,当一个应用程序突然停止进程时,很明显出现了资源耗尽,但是仅通过查看代码无法明确知道何处出现资源耗尽,且为什么会发生。
jstack 是一个可以返回在应用程序上运行的各种各样线程的一个完整转储的实用程序,您可以使用它查明问题。
采用期望进程的 VMID 运行 jstack 会产生一个堆转储。就这一点而言,jstack 与在控制台窗口内按 Ctrl-Break 键起同样的作用,在控制台窗口中,Java 进程正在运行或调用 VM 内每个 Thread 对象上的 Thread.getAllStackTraces() 或 Thread.dumpStack()。jstack 调用也转储关于在 VM 内运行的非 Java 线程的信息,这些线程作为 Thread 对象并不总是可用的。
jstack 的 -l 参数提供了一个较长的转储,包括关于每个 Java 线程持有锁的更多详细信息,因此发现(和 squash)死锁或可伸缩性 bug 是极其重要的。
----------------------------------------------------
JVM内存状况查看方法和分析工具
Eclipse Memory Analyzer
Eclipse Memory Analyzer是Eclipse提供的一个用于分析jvm堆dump文件的插件,借助这个插件可查看对象的内存占用状况、引用关系、分析内存泄露等。
Eclipse Memory Analyzer(MAT)的网站为:Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation,在eclipse中可以直接远程安装此插件。不过由于此插件在分析堆dump文件时比较耗内存,因此在分析前最好先将eclipse的jvm的内存设置大一点,MAT分析dump文件后的对象占用内存及引用关系如图3.23所示。
相对而言MAT功能比jhat强大很多,分析的速度也快一些,因此,如果要分析jvm堆dumap文件,首选推荐的是MAT。
在进行JVM内存状况分析时,通常要关注的主要有GC的趋势、内存的具体消耗状况。
GC趋势对于可图形界面连到需查看GC状况的机器的情况而言,VisualVM是常用的选择;对于不能采用图形界面方式的,输出GC日志 及采用jstat命令直接分析是常用的选择。
在查找内存是程序中的什么对象占用时,需要分析内存的具体消耗状况,对于有图形界面可用的情况,VisualVM是常用的选择;对于不能采用图形界面方式的,可通过jmap dump生成文件后,再通过MAT进行分析是常用的选择。
参考文献:
CMS gc实践总结(纠正并发线程数)
记一次Java GC调整经历
最后附上JVM参数调优的案例,其中批注一下-server的参数使用
.-server
-client
虚拟机服务器模式/客户机模式,使用server模式可以提高性能,启动比client模式慢,长期运行则比client模式快。当该参数不指定时,虚拟机启动检测主机是否为服务器,如果是则以server模式启动,否则以client模式启动,J2SE5.0检测的根据是至少2个CPU和最低2GB内存
jstat pid not found
今天在用jstat命令的时候,遇到pid not found的问题,然,自己确定pid输入是正确存在的,但为什么呢?
原来运维那边每隔一个月,一般在月底都会去清空一下tmp目录下的文件,导致/tmp/hsperfdata_${user}/${PID}文件没了,
user 例如 是admin root等等
pid就是 你当前运行的java的pid了。
顺便说下 如果jstat出现 pid not found ,那用jps也看不到这个进程了。
我这边因为是 这个pid文件被删除了,所以不能看了。
解决方法是重启服务就ok了
----------------------------
JVM 参数优化
-server -Xms2g -Xmx2g -XX:PermSize=96m -XX:MaxPermSize=256m -Xmn1024m -verbose:gc
-Xloggc:/home/yahoo/output/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseConcMarkSweepGC
-XX:+DisableExplicitGC
参数 | 说明 |
-server | 服务机模式,不配置默认的是-client,一定要作为第一个参数,在多个CPU时性能佳 |
-Xms2g -Xmx2g | 堆大小,线上的堆的最大值与最小值必须一致,省得jvm调整堆大小浪费性能。 |
-Xmn1024m | 堆中新生代大小,一般为堆的一半多些 |
-XX:PermSize=96m -XX:MaxPermSize=256m | jvm方法区空间大小 |
-Xloggc:/home/yahoo/output/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps | 打GC日志,这不会给系统带来啥负担,建议线上机器都加上,方便调优和OutOfMemory后查错。 |
-verbose:gc | 将虚拟机的垃圾回收事件信息打印 |
-XX:+UseConcMarkSweepGC | 新生代采用ParNew GC方式,旧生代采用并发GC方式,以减少系统停顿时间为优先,缩短major收集的时间,此选项在Heap Size 比较大而且Major收集时间较长的情况下使用更合适。 |
-XX:+DisableExplicitGC | 禁止程序触发GC,System.gc() |
-Xloggc:gc.log 指定垃圾收集日志文件
-XX:+UseParNewGC :缩短minor收集的时间
HeapDumpOnOutOfMemoryError
在jvm的 启动参数中追加 下列信息,可以在发生 OutOfMemoryError的时候生成 oom.hprof文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:/temp/oom.hprof
直接内存被占满的话,也会导致FULLGC