java JVM 内存溢出

来源:互联网 发布:virtualbox安装ubuntu 编辑:程序博客网 时间:2024/06/05 02:20

内存溢出分类: 1   栈溢出;   2  堆溢出; 3 永久代溢出; 4  outofMemory

java虚拟机规范规定了JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等。  而Hostpot JVM 的实现中,将堆内存分为了两部,新生代,老年代。在堆内存中,还有永久代,其中永久代实现了规范中的规定 的方法区,而内存模式中不同的部分都会出现相应的OOM错误。

栈溢出(StackOverflowError)

原因:方法执行 的时候栈的深度超过了虚拟机容许的最大深度。

一般是由于程序错误引起的,比如写了一个死递归,就有可能造成这种情况。

示例代码:

import java.util.*;    import java.lang.*;    public class OOMTest{         public void stackOverFlowMethod(){            stackOverFlowMethod();        }        public static void main(String... args){            OOMTest oom = new OOMTest();            oom.stackOverFlowMethod();        }    

对于栈内存溢出,根据《Java 虚拟机规范》中文版:_如果线程请求的栈容量超过栈允许的最大容量的话,Java 虚拟机将抛出一个StackOverflow异常;如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。


堆溢出:
堆溢出的时候,虚拟机会抛出java.lang.outofMemoryError:java heap space, 出现这种情况的时候,我们需要根据内存溢出产生的dump文件来具体分析。出现这种问题的时候,有可能是内存泄漏,也有可能是内存溢出了。
  • 如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。
  • 如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。下面我们通过如下的代码来演示一下此种情况的溢出:
import java.util.*; import java.lang.*; public class OOMTest{ public static void main(String... args){ List<byte[]> buffer = new ArrayList<byte[]>(); buffer.add(new byte[10*1024*1024]); } }
运行上面的代码:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
输出的信息:
[GC 1180K->366K(19456K), 0.0037311 secs] [Full GC 366K->330K(19456K), 0.0098740 secs] [Full GC 330K->292K(19456K), 0.0090244 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at OOMTest.main(OOMTest.java:7)
当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,触发垃圾收集,收集后还是不能放下
就会抛出内存溢出异常了。

持久带溢出:Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。

我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况:

import java.util.*;    import java.lang.*;    public class OOMTest{            public static void main(String... args){                    List<String> list = new ArrayList<String>();                    while(true){                            list.add(UUID.randomUUID().toString().intern());                    }            }        }    

OutOfMemoryError:unable to create native thread

最后我们在来看看java.lang.OutOfMemoryError:unable to create natvie thread这种错误。 出现这种情况的时候,一般是下面两种情况导致的:

  1. 程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。
  2. 给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。

我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。_线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存_
通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。


0 0