zoukankan      html  css  js  c++  java
  • 一次内存溢出的填坑经历(转)

    在项目运行过程中,可能会出现内存溢出,内存溢出的原因多种多样,而在内存溢出后,我们如何查找和分析内存溢出的原因呢?这里来说一说我遇到的次遇到的内存溢出经历。

    大致情况是这样的:应用在启动后,过一段时间(这个时间不确定),内存忽然爆满,然后频繁的YGC,一会过后,老年代爆满,然后是频繁的FGC,最终撑爆内存,抛出OOM。重启应用后,还是这个过程。

    1、查看java进程的内存使用情况和GC情况

    通过jastat工具查看GC情况,以及各generation占用比例:

    jstat -gcutil <pid> 1000

    通过观察分析是否是JVM的运行内存分配不合理,可通过新生代、老年代内存占用情况,以及GC情况观察得出。

    如果FGC很频繁,说明老年代被迅速占用满,这有可能是老年代本身分配的内存太小,或者是内存中大量对象被引用,导致无法被YGC等等,最终这些对象都进入老年代导致老年代爆满。

    要分析是什么原因造成的,可以获取内存溢出时JVM的内存快照,通过对快照的分析,来定位内存溢出的原因。

    通过jmap查看JVM堆内存概况:

     

    jmap -heap <pid>

    这个内存概况会根据使用的垃圾收集器的不同而有所差异。

    2、获取jvmdump

    获取jvmdump可通过添加应用服务器(如tomcat)启动参数:

     

    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/mikan/jvmdump

    配置该参数后,在内存溢出时,JVM就会自动dump出当时的内存快照到HeapDumpPath指定的目录。

    也可通过jmap在线获取内存dump,只是在使用jmap获取dump期间应用会被卡住,这可能对线上应用会有影响。如

     

    jmap -J-d64 -dump:format=b,file=/home/mumq/jvmdump.hprof <pid>

    其中pid为java进程的ID。

    获取到JVM内存快照后,可以使用相关的工具来对它进行分析。

    3、分析jvmdump

    jvmdump文件一般比较大(几个G),所以在做离线分析时,千万别在服务器上,因为在解析jvmdump时,会占用极大的内存,如果在服务器上,很可能影响线上应用的正常运行。所以一般情况是down到空闲的机器上做离线分析。

    分析jvmdump的工具有很多,这里推荐使用的是Memory Analyzer (MAT),下载地址是:http://www.eclipse.org/mat/downloads.php

    当前最新Release版本是1.4.0,提供了两种版本:eclipse插件和独立版本,都可通过上面链接下载。

    这里以独立版本为例。在开始分析dump文件之前,先设置MAT的内存,可根据分析的dump文件大小来设置,如果分析的dump文件为2G,那设置2G内存的话,那么解析dump文件生成report会很快,否则,等几个小时都不知道。。

    如何设置?在与MAT相同目录下的MemoryAnalyzer.ini文件中添加如下配置:

     

    1.  
      -vmargs
    2.  
      -Xms2048m
    3.  
      -Xmx4096m

    具体MAT的使用方法,可以直接查看MAT自带的示例教程,相对来说还是比较全面的。

    在MAT解析完内存快照后,会生成相应的report,通过report可以看到当时内存使用情况、内存溢出的可疑之处(Leak Suspects)。如:


    点击Leak Suspects,可以查看到具体的详细信息:


    点击detail,查看详细信息,通过 Accumulated Objects by Class in Dominator Tree列表可以看到:


    User对象有10W+,怎么会这样呢?通过 Accumulated Objects in Dominator Tree列表可以看到:


    是调用了UserServiceImpl.login方法,一次性查询出了10W+的用户数据。如果频繁的调用该方法,则会很快的导致OOM。再查看代码发现在调用login方法时,如果用户名参数传null时,会查询出所有用户名为null的用户。这在数据量还很小或者请求量不大的时候,可能不会有什么问题,一旦数据量上来了,或请求量上来了,那很快就会出现OOM。

    找到问题原因,如何解决就容易了。所以在写代码的时候如果考虑不周全,就有可能导致这种OOM的bug。

    血的教训啊。

  • 相关阅读:
    【转】双口RAM
    Beep使用
    fcntl函数
    ioctl() 参数
    线程属性:pthread_attr_t
    GPIO
    Linux CGI编程基础
    看门狗watchdog
    Linux库知识大全
    linux进程间通讯
  • 原文地址:https://www.cnblogs.com/snowwhite/p/9471766.html
Copyright © 2011-2022 走看看