在开发过程中,应该都会碰到过java.lang.OutOfMemoryError: XXX,这是因为Java程序运行期间有新对象请求内存空间,但是分配给Java虚拟机的内存不够用了,抛出了内存溢出的错误。

JVM规范中对JVM的运行时内存模型分的比较细致,比如什么数据从栈中分配内存并保存在栈中,什么数据从堆中分配内存保存在堆中,有兴趣可以深入阅读规范和相关文章,这里做简单介绍。在平时开发中可能碰到内存溢出涉及的三部分:JVM栈(stack)和堆(Heap),还有方法区(Method Area,规范中规定了只说明方法区逻辑上属于堆,各JVM可以有不同的实现)。

以下说明皆以OracleHotSpot虚拟机为前提,不同虚拟机的实现在内存管理上有所不同,在参数设置上也会有相应的差异。


保存的是本地变量,方法的返回结果等。其大小可以通过参数-Xss来调节(两个参数用一个即可,是等价的),其默认值根据操作系统和JDK版本的不同而不同,一般是512K或者1024K

当执行一个深度比较大甚至是无限循环的递归,或者开的线程太多时可能会抛出java.lang.StackOverflowError的错误,这说明栈空间不够了。如果不是代码Bug原因导致的,可适当调节栈的大小,设置示例:-Xss1536K


保存的就是对象了。(Hotspot的实现中,将堆分了3部分,年轻代,年老代和永久代)堆的大小可以通过-X参数来设置,常用的为-Xms-Xmx来设置堆的初始大小和最大值

如果堆空间分配过小,或是程序中分配了大量对象且垃圾回收期因不能及时回收或者程序Bug导致的不能回收,而导致堆空间无法为新对象分配内存,这种情况下JVM会抛出ava.lang.OutOfMemoryError: heap space 的错误。如果不是代码Bug原因导致的,可适当调节栈的大小,设置示例:-Xms512M –Xmx1024M


方法区

保存的是类的常量池,类变量,方法或者构造器的代码等等的元数据。(Hotspot虚拟机是以永久代来实现方法区)其大小可以通过参数-XX:PermSize -XX:MaxPermSize来设置其大小和最大值。

如果项目中加载了数量庞大的类或者类加载器的实现存在缺陷尤其是动态加载类时,旧的类不能被垃圾回从而出现永久代区域无法分配内存的,这种情况下JVM会抛出java.lang.OutOfMemoryError: PermGen space的错误。视情况可以适当调节永久代的大小,设置示例:-XX:PermSize=128M -XX:MaxPermSize=256M

注意:JDK8中已经移除了永久代,取而代之的是元空间(Metaspace使用本地内存来存储类元数据信息。默认情况下,类元数据只受可用的本地内存限制。如果设置了PermSize MaxPermSize 则会被忽略并给出警告


如果出现内存溢出错误,请检查是不是代码的 Bug引起,不能一味的通过配置来解决,这样治标不治本,甚至不治标。