如何在JVM上调试本机内存中的泄漏?

时间:2016-07-01 17:05:56

标签: memory jvm

我们在Mule上运行了一个java应用程序。我们将XMX值配置为6144M,但通常会看到整体内存使用量攀升和爬升。在我们主动重新启动之前,它在前一天接近20 GB。

Thu Jun 30 03:05:57 CDT 2016
top - 03:05:58 up 149 days,  6:19,  0 users,  load average: 0.04, 0.04, 0.00
Tasks: 164 total,   1 running, 163 sleeping,   0 stopped,   0 zombie
Cpu(s):  4.2%us,  1.7%sy,  0.0%ni, 93.9%id,  0.2%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  24600552k total, 21654876k used,  2945676k free,   440828k buffers
Swap:  2097144k total,    84256k used,  2012888k free,  1047316k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 3840 myuser  20   0 23.9g  18g  53m S  0.0 79.9 375:30.02 java

jps命令显示:

10671 Jps
3840 MuleContainerBootstrap

jstat命令显示:

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
37376.0 36864.0 16160.0  0.0   2022912.0 1941418.4 4194304.0   445432.2  78336.0 66776.7    232    7.044  17     17.403   24.447

启动参数是(敏感位已更改):

3840 MuleContainerBootstrap -Dmule.home=/mule -Dmule.base=/mule -Djava.net.preferIPv4Stack=TRUE -XX:MaxPermSize=256m -Djava.endorsed.dirs=/mule/lib/endorsed -XX:+HeapDumpOnOutOfMemoryError -Dmyapp.lib.path=/datalake/app/ext_lib/ -DTARGET_ENV=prod -Djava.library.path=/opt/mapr/lib -DksPass=mypass -DsecretKey=aeskey -DencryptMode=AES -Dkeystore=/mule/myStore -DkeystoreInstance=JCEKS -Djava.security.auth.login.config=/opt/mapr/conf/mapr.login.conf -Dmule.mmc.bind.port=1521 -Xms6144m -Xmx6144m -Djava.library.path=%LD_LIBRARY_PATH%:/mule/lib/boot -Dwrapper.key=a_guid -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.disable_console_input=TRUE -Dwrapper.pid=10744 -Dwrapper.version=3.5.19-st -Dwrapper.native_library=wrapper -Dwrapper.arch=x86 -Dwrapper.service=TRUE -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1 -Dwrapper.lang.domain=wrapper -Dwrapper.lang.folder=../lang

从jps中添加“capacity”项目表明只有我的6144m用于java堆。什么是使用的内存的其余部分?堆栈内存?原生堆?我甚至不确定如何继续。

如果继续增长,它将消耗系统上的所有内存,我们最终会看到系统冻结,导致交换空间错误。

我有另一个进程正在开始增长。目前居民记忆力约为11g。

pmap 10746 > pmap_10746.txt
cat pmap_10746.txt | grep anon | cut -c18-25 | sort -h | uniq -c | sort -rn | less

Top 10 entries by count:
    119     12K
    112   1016K
     56      4K
     38 131072K
     20  65532K
     15 131068K
     14  65536K
     10    132K
      8  65404K
      7    128K


Top 10 entries by allocation size:
     1 6291456K
      1 205816K
      1 155648K
     38 131072K
     15 131068K
      1 108772K
      1  71680K
     14  65536K
     20  65532K
      1  65512K

And top 10 by total size:
Count   Size    Aggregate
1   6291456K    6291456K
38  131072K 4980736K
15  131068K 1966020K
20  65532K  1310640K
14  65536K  917504K
8   65404K  523232K
1   205816K 205816K
1   155648K 155648K
112 1016K   113792K

这似乎告诉我,因为Xmx和Xms设置为相同的值,所以java堆的单个分配为6291456K。其他分配不是Java堆内存。这些是什么?它们被分配到相当大的块中。

2 个答案:

答案 0 :(得分:3)

扩展有关彼得答案的更多细节。

您可以从VisualVM中获取二进制堆转储(右键单击左侧列表中的进程,然后在堆转储上 - 它将在不久之后出现在下面)。如果无法将VisualVM连接到JVM,则还可以使用以下命令生成转储:

jmap -dump:format=b,file=heap.hprof $PID

然后复制该文件并使用Visual VM打开它(文件,加载,选择类型堆转储,找到该文件。)

正如彼得指出的那样,泄漏的可能原因可能是未收集的DirectByteBuffers(例如:另一个类的某个实例未正确地取消引用缓冲区,因此它们永远不会被GC)。

要确定这些引用的来源,可以使用Visual VM检查堆并在“类”选项卡中查找DirectByteByffer的所有实例。找到DBB类,右键单击,转到实例视图。

这将为您提供实例列表。你可以点击一个,看看每个人都有参考:

Visual VM - Instances view

注意底部窗格,我们有“Cleferener”和“mybuffer”类型的“referent”。这些是引用我们钻进的DirectByteBuffer实例的其他类中的属性(如果忽略Cleaner并专注于其他类,则应该没问题。)

从现在开始,您需要根据您的申请继续进行。

获取DBB实例列表的另一种等效方法来自OQL选项卡。这个查询:

select x from java.nio.DirectByteBuffer x

为我们提供与以前相同的清单。使用OQL的好处是可以执行更多more complex queries。例如,这将获取所有保持对DirectByteBuffer的引用的实例:

select referrers(x) from java.nio.DirectByteBuffer x

答案 1 :(得分:1)

您可以做的是进行堆转储并查找从堆中存储数据的对象,例如ByteBuffers。这些对象看起来很小但是代表更大的堆外存储区域。看看你是否可以确定为什么可以保留很多这些。