探究JVM内存泄露
WEB 服务总是莫名其妙的运行一段时间后 JVM 直接 OutOfMemory 错误,内存泄漏的问题不容易查找,本文就一些查找内存泄露基本知识做个总结,未涉及到具体案例的分析。
1 JVM 内存异常的数据显示
1.1 java.lang.OutOfMemoryError: PermGen space 异常的例子
Heap PSYoungGentotal44928K,used916K[0x4e3c0000,0x50fe0000,0x51b10000) edenspace44736K,2%used[0x4e3c0000,0x4e4a5318,0x50f70000) fromspace192K,0%used[0x50f70000,0x50f70000,0x50fa0000) tospace192K,0%used[0x50fb0000,0x50fb0000,0x50fe0000) PSOldGentotal453312K,used125529K[0x32910000,0x4e3c0000,0x4e3c0000) objectspace453312K,27%used[0x32910000,0x3a3a6498,0x4e3c0000) PSPermGentotal65536K,used65535K[0x2e910000,0x32910000,0x32910000) object space 65536K, 99% used [0x2e910000,0x3290fff8,0x32910000) |
permanent space 持久空间 : 用于类和方法对象的存储。 spring 在 AOP 时使用 CBLIB 会动态产生很多类,当类太多,超过 MaxPermSize 的时候,就会抛出此异常。 参数问题 可以设置 jvm 启动参数 : PermSize, MaxPermSize 。程序问题就要进行内存分析了,详见下文。
1.2 java.lang.OutOfMemoryError: Java heap space 异常的例子
Heap PSYoungGentotal88320K,used67673K[0x44880000,0x4ba40000,0x4ba40000) edenspace61952K,100%used[0x44880000,0x48500000,0x48500000) fromspace26368K,21%used[0x48500000,0x48a96490,0x49ec0000) tospace24512K,16%used[0x4a250000,0x4a6283e0,0x4ba40000) PSOldGentotal932096K,used582090K[0x0ba40000,0x44880000,0x44880000) objectspace932096K,62%used[0x0ba40000,0x2f2b2a78,0x44880000) PSPermGentotal131072K,used35124K[0x03a40000,0x0ba40000,0x0ba40000) object space 131072K, 26% used [0x03a40000,0x05c8d330,0x0ba40000) |
eden space 使用率 100% ,总是被占满,参数问题 可以设置 jvm 启动参数 : Xms, Xmx 。程序问题就要进行内存分析了,详见下文。
1.3 查看 jvm 内存状态:
jstat -gcutil pid 1000 20
异常情况的例子
jstat -gcutil pid 1000 20
S0 S1 E O P YGC YGCT FGC FGCT GCT 0.000.0099.9982.5153.1124091.205101177250.3937251.598 0.000.0083.4282.5553.1024091.205101187252.6507253.855 0.000.0056.0682.4653.1024101.205101207254.4677255.672 0.000.0032.1182.5553.1024111.205101217256.6737257.877 0.000.0099.9982.5553.1024121.205101237257.0267258.231 0.00 0.00 76.00 82.50 53.10 2412 1.205 10124 7259.241 7260.446 |
这个数据显示 Full GC 频繁发生。 |
正常情况的例子
S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031 |
参数含义: S0:Heap上的Survivorspace0段已使用空间的百分比 S1:Heap上的Survivorspace1段已使用空间的百分比 E:Heap上的Edenspace段已使用空间的百分比 O:Heap上的Oldspace段已使用空间的百分比 P:Permspace已使用空间的百分比 YGC:从程序启动到采样时发生YoungGC的次数 YGCT:YoungGC所用的时间(单位秒) FGC:从程序启动到采样时发生FullGC的次数 FGCT:FullGC所用的时间(单位秒) GCT :用于垃圾回收的总时间 ( 单位秒 ) |
2 Dump 出内存
2.1 找出要 dump 的线程 pid
在 windows 下,使用 tasklist |
在 Linux 下,使用 ps –aux |
2.2 Dump 出内存使用详情
可以通过命令:
jmap -dump:file=a.hprof pid |
也可以通过 jconsole 的图形界面操作。
在命令行键入: jconsole |
Jconsole 打开后在造作下选择 dumpHeap, 输入参数 p0,p1;p0 表示 dump 出来的文件路径,后缀为 .hprof ;p1 设为 true ,表示只分析活着的对象。
3 使用内存分析工具
目前有很多用来分析 Java 内存对象的工具,如收费的工具有 jprofiler, 而像 Eclipse MAT 则是优秀的内存对象分析开源工具 . 它们对于分析内存溢出问题非常有用。以下是一个安装使用 Eclipse MAT 的简单例子。
3.1 装一个 Eclipse 的内存分析插件 MAT
http://download.eclipse.org/technology/mat/latest/update-site/
3.2 切换到 Memory Analysis 模式
3.3 通过 File > Open Heap Dump.... 查看 dump 出来的文件
4 JDK 自带的 JVM 查看分析工具 jps 、 jmap 、 jstat 、 jconsole
4.1 jps
Java 进程查看工具,实际上它和 Unix/Linux 上面的 ps 命令的功能差不多
4.2 jmap
jmap 是一个可以输出所有内存中对象的工具 .
* -dump:format=b,file=<filename> 转存堆内存到本地文件。 * -histo 打印堆里每个类的情况,包含内存占用大小、对象数量及完整类名。 VM 的内部类以 "*" 开头。
例子:
jmap -histo pid>a.log |
jmap -dump: file=a.hprof pid |
查看 a.log
num #instances #bytes class name -------------------------------------- 1:42739814458448[I 2:1787986830216[C 3:502786668512<constMethodKlass> 4:1799244318176java.lang.String 5:502784026648<methodKlass> 6:152443894200[B 7:478091773776[Ljava.lang.Object; ... Total 1645187 81806088 |
说明: #instance 是对象的实例个数 #bytes是总占用的字节数 classname对应的就是Class文件里的class的标识 B代表byte C代表char D代表double F代表float I代表int J代表long Z代表boolean 前边有 [ 代表数组, [I 就相当于 int[]对象用 [L+ 类名表示 |
4.3 jstat
jstat 是 vm 的状态监控工具,监控的内容有类加载、运行时编译及 GC 。
使用时,需加上查看进程的进程 id ,和所选参数。以下详细介绍各个参数的意义。
jstat-classpid:显示加载class的数量,及所占空间等信息。
jstat-compilerpid:显示VM实时编译的数量等信息。
jstat-gcpid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是younggc的次数,younggc的时间,fullgc的次数,fullgc的时间,gc的总时间。
jstat-gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推,OC是old内纯的占用量。
jstat-gcnewpid:new对象的信息。
jstat-gcnewcapacitypid:new对象的信息及其占用量。
jstat-gcoldpid:old对象的信息。
jstat-gcoldcapacitypid:old对象的信息及其占用量。
jstat-gcpermcapacitypid:perm对象的信息及其占用量。
jstat-utilpid:统计gc信息统计。
jstat-printcompilationpid:当前VM执行的信息。
除了以上一个参数外,还可以同时加上两个数字,如:jstat-printcompilation30242506是每250毫秒打印一次,一共打印6次,还可以加上-h3每三行显示一下标题。
例子:jstat -gcutil pid 1000 20 |
4.4 jconsole
一个 java GUI 监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器 VM 。