JVM在这些情形下会抛出一个内存溢出错误:
(1)JVM没有可用的native memory。
(2)permgen(java 7和早期版本)或metaspace(java8)内存溢出了
(3)Java heap自身内存溢出了:在给定的heap size下,应用程序中有太多的活跃对象。
(4)JVM花费大量时间在执行GC
最后2种情况——涉及到Java heap自身——更常见,但不会自动得出结论说heap就是问题所在。所以有必要查找为什么会发生内存溢出(有一部分原因是异常的输出)。
一、Out of native memory
这和heap一点关系都没有。在一个32位的JVM中,一个进程的最大size是4GB(一些版本的Windows中是3GB,一些老版本的Linux上是3.5GB)。假如,你指定一个非常大的heap,
3.8GB——这就使得应用程序的size危险地接近那个限制了。甚至在一个64位JVM中,操作系统可能都没有足够的虚拟内存来应付JVM请求。
该主题在第8章会详细介绍。你需要记住如果内存溢出的消息说的是native memory,那么heap tuning不是该问题的答案。你需要仔细看一下错误中提到的native memory issue。
例如,下面的消息告诉你,线程栈的native memory耗尽了:
Exception in thread "main" java.lang.OutOfMemoryError:
unable to create new native thread
二、Out of permgen or metaspace memory
这和heap也没关系。发生此错误是因为permgen(java 7)或metaspace native memory(java 8)满了。错误的根源可能是这2方面:1.应用程序用了太多的classes,超过了默认的
perm space。解决方案就是加大permgen的size。2.更棘手一些,它涉及到classloader内存溢出。大多发生在一个JavaEE应用服务器中。每个部署到应用服务器的应用程序都运行在
它自己的classloader中(提供了隔离性,一个应用中的类不会和另一个应用中的类间产生共享或干预)。在开发过程中,每次应用改动,都必须重新部署:一个新的classloader会
被创建用来加载新的classes,同时老的classloader is allowed to go out of scope。一旦老的classloader goes out of scope,class metadata就可以被收回了。
如果老的classloader没有go out of scope,则class metadata就不会被释放,最终permgen将被充满,就会抛出内存溢出错误。这种情况下,加大permgen的size会有所帮助,但这
这是延缓了错误的发生。
如果该情况发生在一个应用服务器环境中,能做的很少,你需要联系应用服务器提供商,让他们修复这个问题。如果你正在编写你自己的应用程序,该程序创建并抛弃大量的classloaders,那么你要小心确保class loaders自身要正确地被抛弃。要调试该情形,heap dump analysis就很有用:在直方图中,找出ClassLoader类的所有实例,并跟踪它们的
GC roots来获知what is holding onto them。
Java 8中,抛出的错误文本是:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
Java 7中,是:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
三、Out of heap memory(。。。。。。。。。。。。。。。。。。。待续)