Java内存泄露问题

对于内存泄露问题如果处理不当很容易造成down机,尤其是应用服务器(Java容器)上出现内存泄漏经常给人一种天要塌下来的感觉。下面从四个方面来系统说一下内存泄露。

1. 为什么会出现内训泄露问题

编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到Heap内存不够用时,jvm会进行垃圾回收,清除这些对象占用的Heap内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时,无法从Heap中获取足够的内存分配给对象,这时候就会导致内存溢出。而出现内存泄露的地方,一般是不断的往容器中存放对象,而容器没有相应的大小限制或清除机制。容易导致内存溢出。

2. 内存泄漏的现象:

程序内存泄漏最基本的表现就是程序中得到一个OutOfMemoryError。在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。在《彻底搞懂内存泄露》这篇文章详细介绍了内存泄露的情况

3. 发现内存泄漏

3.1常用命令

  1. jstat -gc pid

     可以显示gc的信息,查看gc的次数,及时间。
    
     其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
    
  2. jstat -gccapacity pid

  可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,

   如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,

   PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。

   其他的可以根据这个类推, OC是old内纯的占用量。
  1. jstat -gcutil pid

       统计gc信息统计。
    
  2. jstat -gcnew pid

       年轻代对象的信息。
    
  3. jstat -gcnewcapacity pid

        年轻代对象的信息及其占用量。
    

3.2 一个Java内存泄漏的排查案例

某个业务系统在一段时间突然变慢,我们怀疑是因为出现内存泄露问题导致的,于是踏上排查之路。
1)确定频繁Full GC现象
首先通过“虚拟机进程状况工具:jps”找出正在运行的虚拟机进程,最主要是找出这个进程在本地虚拟机的唯一ID(LVMID,Local Virtual Machine Identifier),因为在后面的排查过程中都是需要这个LVMID来确定要监控的是哪一个虚拟机进程。

同时,对于本地虚拟机进程来说,LVMID与操作系统的进程ID(PID,Process Identifier)是一致的,使用Windows的任务管理器或Unix的ps命令也可以查询到虚拟机进程的LVMID。
jps命令格式为:

jps [ options ] [ hostid ]

使用命令如下:

使用jps:jps -l

使用ps:ps aux | grep tomat找到你需要监控的ID(假设为20954),再利用“虚拟机统计信息监视工具:jstat”监视虚拟机各种运行状态信息。

jstat命令格式为:

jstat [ option vmid [interval[s|ms] [count]] ]

使用命令如下:

jstat -gcutil 20954 1000

意思是每1000毫秒查询一次,一直查。gcutil的意思是已使用空间站总空间的百分比。
结果如下图:

在这里插入图片描述

jstat执行结果

查询结果表明:这台服务器的新生代Eden区(E,表示Eden)使用了28.30%(最后)的空间,两个Survivor区(S0、S1,表示Survivor0、Survivor1)分别是0和8.93%,老年代(O,表示Old)使用了87.33%。程序运行以来共发生Minor
GC(YGC,表示Young GC)101次,总耗时1.961秒,发生Full GC(FGC,表示Full GC)7次,Full
GC总耗时3.022秒,总的耗时(GCT,表示GC Time)为4.983秒。

2)找出导致频繁Full GC的原因

分析方法通常有两种:

1)把堆dump下来再用MAT等工具进行分析,但dump堆要花较长的时间,并且文件巨大,再从服务器上拖回本地导入工具,这个过程有些折腾,不到万不得已最好别这么干。

2)更轻量级的在线分析,使用“Java内存影像工具:jmap”生成堆转储快照(一般称为headdump或dump文件)。

jmap命令格式:

jmap [ option ] vmid

使用命令如下:

jmap -histo:live 20954

查看存活的对象情况,如下图所示:

存活对象

按照一位IT友的说法,数据不正常,十有八九就是泄露的。在我这个图上对象还是挺正常的。

我在网上找了一位博友的不正常数据,如下:

在这里插入图片描述

可以看出HashTable中的元素有5000多万,占用内存大约1.5G的样子。这肯定不正常。

3)定位到代码

定位带代码,有很多种方法,比如Eclipse
MAT的出现使这个问题变得非常简单。EclipseMAT是著名的SAP公司贡献的一个工具,可以在Eclipse网站下载到它,完全免费的。通过MAT查看Histogram即可找出是哪块代码。也可以使用BTrace。

4 如何规避内存泄露问题

好的编码实践可能会大大降低内存溢出的产生。
1. 编码规范认真执行。找几个资深程序猿(或者整个项目组讨论后)写一个Java编码规范,让项目组成员尽量遵守。一目了然的代码更容易定位问题,当然也更能让人写出好的代码。
2. 单元测试要覆盖所有分支与边界条件。不要拿某种情况不会出现做借口。有句老话说常在河边站哪有不湿鞋(学名墨菲定律)。
3. 代码审查要走。代码写完了,找资深程序猿扫扫代码没有坏处。
4. 有条件的项目组要充分利用测试人员的能动性。
5. 如果项目的期望较高,就把上面的尽量、可能等词汇改成一定要。

以上五条建议对非性命攸关型项目足够了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值