Java内存溢出异常:加载zip文件时的mmap错误

11

我在生产环境中运行我的应用程序(rhel 5.2 x64,oracle jre 1.7_05,tomcat 7.0.28),使用JVM参数:

-Xms8192m -Xmx8192m -XX:MaxPermSize=1024m 
-Doracle.net.tns_admin=/var/ora_net -XX:ReservedCodeCacheSize=512m -XX:+AggressiveOpts -XX:+UseFastAccessorMethods 
-XX:+UseStringCache -XX:+OptimizeStringConcat -XX:+UseCompressedOops -XX:+UseG1GC -Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9026 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

经过多次尝试,我得到了以下的堆栈跟踪:

Java HotSpot(TM) 64-Bit Server VM warning: Attempt to deallocate stack guard pages failed.
Java HotSpot(TM) 64-Bit Server VM warning: Attempt to allocate stack guard pages failed.
mmap failed for CEN and END part of zip file
[...]
Caused by: java.lang.OutOfMemoryError: null
    at java.util.zip.ZipFile.$$YJP$$open(Native Method) ~[na:1.7.0_05]
    at java.util.zip.ZipFile.open(Unknown Source) ~[na:1.7.0_05]
    at java.util.zip.ZipFile.<init>(Unknown Source) ~[na:1.7.0_05]
    at java.util.zip.ZipFile.<init>(Unknown Source) ~[na:1.7.0_05]
    at java.util.jar.JarFile.<init>(Unknown Source) ~[na:1.7.0_05]
    at java.util.jar.JarFile.<init>(Unknown Source) ~[na:1.7.0_05]
    at sun.net.www.protocol.jar.URLJarFile.<init>(Unknown Source) ~[na:1.7.0_05]
    at sun.net.www.protocol.jar.URLJarFile.getJarFile(Unknown Source) ~[na:1.7.0_05]
    at sun.net.www.protocol.jar.JarFileFactory.get(Unknown Source) ~[na:1.7.0_05]
    at sun.net.www.protocol.jar.JarURLConnection.connect(Unknown Source) ~[na:1.7.0_05]
    at sun.net.www.protocol.jar.JarURLConnection.getInputStream(Unknown Source) ~[na:1.7.0_05]
    at java.net.URL.openStream(Unknown Source) ~[na:1.7.0_05]
    at org.apache.catalina.loader.WebappClassLoader.findLoadedResource(WebappClassLoader.java:3279) ~[na:na]
    at org.apache.catalina.loader.WebappClassLoader.getResourceAsStream(WebappClassLoader.java:1478) ~[na:na]
    at org.apache.http.util.VersionInfo.loadVersionInfo(VersionInfo.java:242) ~[httpcore-4.2.jar:4.2]
    at org.apache.http.impl.client.DefaultHttpClient.setDefaultHttpParams(DefaultHttpClient.java:180) ~[httpclient-4.2.jar:4.2]
    at org.apache.http.impl.client.DefaultHttpClient.createHttpParams(DefaultHttpClient.java:158) ~[httpclient-4.2.jar:4.2]
    at org.apache.http.impl.client.AbstractHttpClient.getParams(AbstractHttpClient.java:448) ~[httpclient-4.2.jar:4.2]

查看了我的分析工具,一切都很正常(堆和非堆内存使用率为10%),但我不知道问题出在哪里。

这个问题每天都会在同一时间发生,而且与应用程序的运行时间无关。是什么导致了这个问题?

修改后:

日志文件中有新的输出:

Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=
Code Cache  [0x00002aaaab790000, 0x00002aaaad240000, 0x00002aaacb790000)
 total_blobs=4223 nmethods=3457 adapters=707 free_code_cache=497085Kb largest_free_block=508887936

但是我有足够的内存:http://i.stack.imgur.com/K8VMx.jpg

答案: 问题在于Java版本。这里有描述:https://forums.oracle.com/forums/thread.jspa?messageID=10369413

4个回答

4
我以前遇到过这些错误,当资源不足时,例如交换空间已满或允许内存映射已用完。请查看sudo cat /proc/$PID/maps | wc -lcat /proc/sys/vm/max_map_count的比较。
请参见下面的评论。
我还建议...
您似乎遇到了YourKit的一个错误。您正在使用哪个版本?
我会缩减大部分选项,因为它们要么是默认值并且无法起到作用,要么会使事情变得更加复杂。
-mx8g -XX:MaxPermSize=1g -Doracle.net.tns_admin=/var/ora_net 
-XX:ReservedCodeCacheSize=512m -XX:+UseG1GC -Dcom.sun.management.jmxremote.port=9026

我建议您尝试删除-XX:+UseG1GC选项,因为这是一种相对较新的垃圾回收器,可能不会改变您的结果。

出于好奇,您是如何确定 Yourkit 部分的呢? - Erik
当资源耗尽时,例如虚拟内存不足或超出允许的内存映射范围,我以前也遇到过这些错误。请查看 sudo cat /proc/$PID/maps | wc -lcat /proc/sys/vm/max_map_count 进行比较。 - Peter Lawrey
是的,我得到了65538/65536。这是个问题 :). 我执行了 sysctl -w vm.max_map_count=100000 但我仍然需要重新启动我的应用程序才能应用更改吗? - Darya Dmitrichenko
我还没有尝试过这个。一旦你遇到错误,我认为你应该重新启动。顺便说一句,我也会尽量减少映射数量,因为64K是很多的。 - Peter Lawrey
2
非常感谢您的回答,帮助我找出了问题的原因。 - Darya Dmitrichenko
显示剩余3条评论

0

以下是原博客文章的部分段落,解释了Java Jar/Zip的工作原理:

OOM错误是在Java JDK ZipFile调用本地方法(ZipFile.open(Native Method))加载我们的应用EAR文件时触发的。这个本地JVM操作需要适当的本地内存和虚拟地址空间才能执行其加载操作。目前的结论是,我们的Java VM 1.5在部署时已经耗尽了本地内存/虚拟地址空间。
使用JDK 1.4 / 1.5时,Java VM加载的任何JAR / ZIP文件都会完全映射到一个地址空间中。这意味着您在单个JVM中加载的EAR / JAR文件越多,您的Java进程的本地内存占用就越高。
这也意味着您的Java Heap和PermGen空间越高,留给本地内存空间(如C-Heap和MMAP Files)的内存就越少,如果您将太多单独的应用程序(EAR文件)部署到单个32位Java进程中,这肯定会成为一个问题。
请注意,Sun在JDK 1.6(Mustang)中进行了改进,并更改了行为,使JAR文件的中央目录仍然被映射,但条目本身是单独读取的,从而减少了本地内存需求。
我建议您查看下面的Sun Bug Id链接,以获取有关此类JDK 1.4 / 1.5限制的更多详细信息。 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6280693

0

不确定Java 1.7有什么变化,因为我记得在Java 1.6中我们使用以下Xms选项。

  -Xms=512m -Xmx=512m

0

尝试这些选项

-Xrunhprof:heap=all,depth=12,cutoff=0

这将在应用程序根目录中生成一个转储文件。稍后您可以使用HP Jmeter进行分析。这将给出您的8G内存发生了什么的快照。您可以在此处查看HP JMeter手册here

同时,明智地选择Xrunhprof选项。我提到的上述选项会生成巨大的转储文件。从手册中,您可以找到适合的选项。


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