Java堆以外分配了大量内存

5
我有一个作为服务器运行的Java程序。 Java版本是Java6u35。 操作系统是CentOS 6。
堆配置为-Xmx3g -Xms3g。 运行几天后,它分配了超过8G的内存并实际使用了超过6G的内存。
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                        
26955 deploy    15   0 8347m 6.0g  10m S 30.9 25.5   3730:12 java 

额外的内存肯定是堆外内存。使用pmap命令,我得到了以下内容,单位为k。整个输出超出了stackoverflow问题正文的限制,因此我删除了一些较小的部分。从1016k大小开始(有超过300行类似的内容:address 1,016 rwx-- [anon]):

addres             size(kB) 
0000000063ba3000    1,016   rwx--   [anon]   (over 300 similar lines omitted)
00002aaaaaac3000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libverify.so
00002aaaaabee000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libjava.so
00002aaaaad2c000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libjdwp.so
00002aaaaae38000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libnpt.so
00002aaab76b1000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libdt_socket.so
00002aaab77b8000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libmanagement.so
00002aaab842b000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libnio.so
00002aaab8748000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libjaas_unix.so
00002aab87519000    1,020   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/librmi.so
00000000411ec000    1,024   rwx--   [anon]
00000000412ed000    1,024   rwx--   [anon]
0000000041ad8000    1,024   rwx--   [anon]
0000000041bd9000    1,024   rwx--   [anon]
0000000041cda000    1,024   rwx--   [anon]
0000000041ddb000    1,024   rwx--   [anon]
0000000041edc000    1,024   rwx--   [anon]
0000000041fdd000    1,024   rwx--   [anon]
00000000420de000    1,024   rwx--   [anon]
00000000421df000    1,024   rwx--   [anon]
4.22E+02    1,024   rwx--   [anon]
00000000423e1000    1,024   rwx--   [anon]
00000000428e6000    1,024   rwx--   [anon]
00002b66d5a07000    1,024   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/server/libjvm.so
00002aaab78cc000    1,028   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libnet.so
00002b66d4fe5000    1,028   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/jli/libjli.so
00002aaaae72f000    1,032   -----   /usr/java/jdk1.6.0_35/jre/lib/amd64/libzip.so
00002aab83efd000    1,036   -----   [anon]
00002aabbfefd000    1,036   -----   [anon]
00000038b5600000    1,340   r-x--   /lib64/libc-2.5.so
00002aaacfe95000    1,452   -----   [anon]
00002aaab74e3000    1,628   r-xs-   /usr/java/jdk1.6.0_35/jre/lib/rt.jar
00002b66d5b07000    1,752   rwx--   /usr/java/jdk1.6.0_35/jre/lib/amd64/server/libjvm.so
00000038b574f000    2,044   -----   /lib64/libc-2.5.so
00000038b5e16000    2,044   -----   /lib64/libpthread-2.5.so
00000038b6a82000    2,044   -----   /lib64/libm-2.5.so
00000038b9a15000    2,044   -----   /lib64/libnsl-2.5.so
00002aaaae520000    2,044   -----   /lib64/libnss_files-2.5.so
00002aaab8223000    2,044   -----   /lib64/libnss_dns-2.5.so
00000038b5a02000    2,048   -----   /lib64/libdl-2.5.so
00000038b7207000    2,048   -----   /lib64/librt-2.5.so
00000038b8211000    2,048   -----   /lib64/libresolv-2.5.so
00002aaab8546000    2,048   -----   /SERVER/lib/native/libhadoop.so
00002aaab79d0000    3,076   rwx--   [anon]
00002aabd3b22000    4,984   -----   [anon]
00002aaab18f5000    6,400   rwx--   [anon]
00002aaaae834000    7,940   rwx--   [anon]
ffffffffff600000    8,192   -----   [anon]
00002b66d50eb000    9,328   r-x--   /usr/java/jdk1.6.0_35/jre/lib/amd64/server/libjvm.so
00002aaac8000000    18,180  rwx--   [anon]
00002aabcc000000    28,724  rwx--   [anon]
00002aabcdc0d000    36,812  -----   [anon]
00002aaaaeff5000    41,216  rwx--   [anon]
00002aaac91c1000    47,356  -----   [anon]
00002aab84000000    54,368  rwx--   [anon]
00002aaaaaf38000    55,120  r-x--   /usr/lib/locale/locale-archive
00002aaab884c000    56,784  rwx--   [anon]
00002aabd0000000    60,552  rwx--   [anon]
00002aaacc000000    64,084  rwx--   [anon]
00002aab80000000    64,500  rwx--   [anon]
00002aabbc000000    64,500  rwx--   [anon]
00002aab14000000    64,576  rwx--   [anon]
00002aab64000000    64,584  rwx--   [anon]
00002aab90000000    64,668  rwx--   [anon]
00002aabac000000    64,684  rwx--   [anon]
00002aab88000000    64,692  rwx--   [anon]
00002aabc4000000    64,716  rwx--   [anon]
00002aab18000000    64,836  rwx--   [anon]
00002aab48000000    64,868  rwx--   [anon]
00002aabc0000000    64,896  rwx--   [anon]
00002aabb4000000    64,936  rwx--   [anon]
00002aabb8000000    64,948  rwx--   [anon]
00002aab94000000    64,976  rwx--   [anon]
00002aab1c000000    65,008  rwx--   [anon]
00002aabb0000000    65,020  rwx--   [anon]
00002aaaf8000000    65,032  rwx--   [anon]
00002aaabc000000    65,152  rwx--   [anon]
00002aab98000000    65,184  rwx--   [anon]
00002aaba0000000    65,320  rwx--   [anon]
00002aab8c000000    65,360  rwx--   [anon]
00002aaac0000000    65,388  rwx--   [anon]
00002aaba8000000    65,440  rwx--   [anon]
00002aaba4000000    65,472  rwx--   [anon]
00002aaac4000000    65,508  rwx--   [anon]
00002aab9c000000    65,508  rwx--   [anon]
00002aaab1f35000    87,736  rwx--   [anon]
000000004a74f000    104,592 rwx--   [anon]
00002aaafc000000    392,432 rwx--   [anon]
00002aab4c000000    392,480 rwx--   [anon]
00002aab68000000    392,908 rwx--   [anon]
00002aab20000000    654,464 rwx--   [anon]
00002aaad0000000    655,072 rwx--   [anon]
738000000   3,276,800   rwx--   [anon]
total   8,551,872       

使用此工具检查直接内存使用情况:https://gist.github.com/rednaxelafx/1593521

JVM version is 20.10-b01
NIO direct memory: (in bytes)
  reserved size = 228.038025 MB (239115200 bytes)
  max size      = 2867.250000 MB (3006529536 bytes)

因此,直接内存并不太大。3,276,800是Java堆。但找不到谁需要数百MB的内存。

此服务器程序使用Hadoop客户端、HBase客户端将数据写入Hadoop和HBase。Java堆本身运行良好。

欢迎提供任何线索。


操作系统管理的是缓冲区还是缓存相关的内存? - PEdroArthur
1
这是一个多线程应用程序吗?你能否获取线程转储并查看有多少个线程(kill -3 <pid>)?请参阅此SO问题:https://dev59.com/b3M_5IYBdhLWcg3wNgOZ - Tim Jones
由操作系统管理的缓冲区不会计入应用程序的内存使用量,就我所记得的。 - DeepNightTwo
是的,服务器有数百个线程在运行。这与1016kb的匿名内存相匹配。感谢您的提示!有一些[anon]分配了X00 MB的内存。我认为那可能是泄漏的原因... - DeepNightTwo
1个回答

7
找到了解决方案,但原因仍然不明确。在添加-XX:MaxDirectMemorySize=1024m后,它可以正常工作。
似乎Java的堆外内存垃圾回收(MOH)并不像堆内GC那样聪明。
猜测原因是当MOH使用达到一定水平时,Java的MOH GC才会触发,这个水平可能是50%左右。因此,在这之前,它只会将内存标记为已释放,而不会真正释放它。即使系统的内存很少。
默认情况下,XX:MaxDirectMemorySize很大,即使使用率达到了50%,系统也会崩溃。
因此,在添加这个限制后,阈值很容易达到,MOH会及时被释放。所以,对于我的应用程序,1024m 是可行的。它足够大,可以运行应用程序,但又不会太大导致系统崩溃。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接