PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 12663 test 20 0 8378m 6.0g 4492 S 43 8.4 162:29.95 java
正如你所看到的,我们有6Gb的驻留内存。 现在有趣的部分是这个过程是用这些参数来执行的:
看着这些设置和实际的内存使用情况,我们无意中发现我们期望这个过程使用的是什么以及它实际使用的是什么。
通常我们的内存问题是通过分析堆转储来解决的,但是在这种情况下,我们的内存是在堆外使用的。
问题:尝试找出如此高内存使用率的原因是什么? 什么工具可以帮助我们识别在这个过程中使用什么内存?
编辑0
看起来这不像是一个堆相关的问题,因为我们在那里还有相当多的空间:
jmap -heap 12663
结果(编辑以节省空间)
Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 2147483648 (2048.0MB) NewSize = 536870912 (512.0MB) MaxNewSize = 536870912 (512.0MB) OldSize = 1610612736 (1536.0MB) NewRatio = 7 SurvivorRatio = 8 PermSize = 21757952 (20.75MB) MaxPermSize = 85983232 (82.0MB) New Generation: 45.7% used Eden Space: 46.3% used From Space: 41.4% used To Space: 0.0% used concurrent mark-sweep generation: 63.7% used Perm Generation: 82.5% used
编辑1
使用pmap我们可以看到有相当多的64Mb的分配量:
pmap -x 12663 | grep rwx | sort -n -k3 | less
结果是:
... a lot more of these 64Mb chunks 00007f32b8000000 0 65508 65508 rwx-- [ anon ] <- what are these? 00007f32ac000000 0 65512 65512 rwx-- [ anon ] 00007f3268000000 0 65516 65516 rwx-- [ anon ] 00007f3324000000 0 65516 65516 rwx-- [ anon ] 00007f32c0000000 0 65520 65520 rwx-- [ anon ] 00007f3314000000 0 65528 65528 rwx-- [ anon ] 00000000401cf000 0 241904 240980 rwx-- [ anon ] <- Direct memory ? 000000077ae00000 0 2139688 2139048 rwx-- [ anon ] <- Heap ?
那么如何找出这些64Mb块呢? 什么是使用他们? 他们有什么样的数据?
谢谢
这个问题可能与这个glibc问题有关 。
基本上,当你有多个线程分配内存,glibc将扩大可用的场所的数量进行分配,以避免争夺locking。 一个竞技场是64Mb大。 上限是创造核心竞技场的8倍。 当一个线程访问一个已经被locking的竞技场,随着时间的推移,竞技场将被创build。
在撒有线程的Java中,这可能很快导致许多舞台被创build。 而且有分配遍布这些领域。 最初,每个64Mb竞技场只是映射了未经整理的内存,但是当你进行分配时,你开始为它们使用实际的内存。
你的pmap可能有类似于下面的列表。 请注意324K + 65212K = 65536K,560K + 64976K == 65536K,620K + 64916K == 65536K。 也就是说,他们总计达64Mb。
00007f4394000000 324K rw --- [anon] 00007f4394051000 65212K ----- [anon] 00007f4398000000 560K rw --- [anon] 00007f439808c000 64976K ----- [anon] 00007f439c000000 620K rw --- [anon] 00007f439c09b000 64916K ----- [anon]
至于解决方法 :该错误提到了一些可以设置来限制竞技场数量的环境参数,但是您需要足够高的glibc版本。
Lamdba探针怎么样? 除此之外,它可以显示内存使用情况,类似于下面的屏幕截图:
有时pmap -x your_java_pid
也可能有帮助。
JProfiler可能是你寻求的东西,但它不是免费的。 另一个用于调查Java进程的内存使用情况的好工具是Java VisualVM,可作为Oracle / Sun JDK发行版中的JDK工具使用。 我个人推荐更全面的方法来解决这个问题(即监控JDK + OS +磁盘等) – 使用一些networking监控系统–Nagios,Verax NMS或OpenNMS。
问题在堆外面,所以最好的候选人是:
JNI leak Allocation of direct memory buffer
由于你限制了直接缓冲区的大小,我认为最好的候选人是JNI泄漏。
有一个方便的工具来查看JDK中包含的堆内存的分配,称为jmap,在这个堆栈上还有堆栈等(Xss)。 运行这两个jmap命令来获取有关内存使用情况的更多信息:
jmap -heap <PID> jmap -permstat <PID>
要获得更多的信息,可以使用jconsole(也包含在JDK中)连接到进程。 但是Jconsole需要在应用程序中configurationJMX。
使用JVisualVM。 它有各种不同的看法,会告诉你有多less堆内存正在使用,PermGen等等。
至于回答你的问题。 Java处理内存与您所期望的完全不同。
当你设置-Xms和-Xmx参数时,你告诉JVM它应该在堆中分配多less内存,以及它应该分配多less内存作为最大值。
如果你有一个Java应用程序使用了总共1m的内存,但是通过了-Xms256m -Xmx2g,那么JVM将使用256m的内存进行初始化。 它不会less用那个。 不要紧,你的应用程序只使用1米的内存。
其次。 在上面的例子中,如果你的应用程序在某个时候使用超过256M的内存,那么JVM就会根据需要分配尽可能多的内存来处理请求。 但是,它不会将堆大小回落到最小值。 至less,不是在大多数情况下。
在你的情况下,因为你设置的最小和最大内存为2g,JVM将在开始时分配2g并维护它。
Java内存pipe理非常复杂,调优内存使用本身就是一个任务。 然而,有很多资源可以帮助。