sun jdk内存泄露解析之java.lang.OutOfMemoryError: Java heap space

这是最常见的OOM错误,当通过new创建对象或数组时,如Java Heap空间不足(新生代不足,触发Young GC,还是不够,触发Full GC,还是不够),则抛出此错误。

既然最常见,更要注意避免。让我们看几个出现这种OOM的示例:

1. 先看code

public  static  void main ( String [ ] args )  throws  Exception {
     new  Thread ( new  Runnable ( )  {
         public  void run ( )  {
            List <byte [ ] > listbytes = new ArrayList <byte [ ] > ( ) ;
             for  ( int i  =  0 ; i  <  4028 ; i ++ )  {
                 System. out. println ( "thread a: " +i ) ;
                listbytes. add ( new  byte [ 1024 ] ) ;
             }
         }
     } ). start ( ) ;
}

以 -Xms20m -Xmx20m -Xmn10m -XX:+UseParallelGC -XX:+ HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -Xloggc:gc.log 参数运行

运行后在输出的日志中可看到大量的Full GC信息,以及:java.lang.OutOfMemoryError: Java heap space

先看看20M的堆内存,怎么分配的:
Heap
PSYoungGen      total 8960K
eden space 7680K
from space 1280K
to   space 1280K
PSOldGen        total 10240K
object space 10240K
PSPermGen       total 12288K
object space 12288K

可以看到:
PSYoungGen(新生代)中,eden+from+to=10240K
total 8960K是因为新生代内存from和to两个space不可能同时使用,所以7680k+1280K=8960K
PSOldGen(老生代),10240K
至于PSPermGen,无视他吧,丫不属于GC管理的堆内存。

再观察一下gc.log中gc的情况,在0.1s左右做了一次Young GC之后,后边疯狂的Full GC(好吧,还不是非常疯狂,否则最后报错就不是Java heap space了,这个以后再说)。注意在Full GC的过程中,PSOldGen一直在上涨,因为“可达”,GC对PSOldGen的清理微不足道。一直上涨,也必然造成内存不足、泄露。

前边我们已经知道这种OOM是在new对象或数组时Java Heap Space不足造成的,现在需要知道的是程序中哪些部分占用了Java Heap。

通过mat来分析程序运行生成的hprof文件。通过dominator_tree,可看到sun.misc.Launcher$AppClassLoader占据了大部分的内存。继续点开,发现根源在一个ArrayList,其中存放的对象占据了大部分的内存:


怎样避免:

这种情况造成的OOM,在实际的场景中当使用集合时很容易产生,程序中对于所有集合(List、Map等等)、缓存(本地Cache)大小都应给定限制的最大大小,以避免出现集合或缓存太大,导致消耗了过多的内存,从而导致OOM

2. 再来一段代码先

public  static  void main ( String [ ] args )  throws  Exception {
     for  ( int i  =  0 ; i  <  10 ; i ++ )  {
         new  Thread ( new  Runnable ( )  {
             public  void run ( )  {
                 byte [ ] bytes  =  new  byte [ 1024 * 1024 * 2 ] ;
                 System. out. println ( Thread. currentThread ( ). getName ( ) ) ;
                 try  {
                     Thread. sleep ( 1000 ) ;
                 } catch  ( InterruptedException e )  {
                    e. printStackTrace ( ) ;
                 }
             }
         } ). start ( ) ;
     }
}

同样,以-Xms20m -Xmx20m -Xmn10m -XX:+UseParallelGC -XX:+ HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -Xloggc:gc.log参数运行

把生成的hprof文件放入mat中进行分析,通过dominator_tree,可以看到,是一堆线程瓜分了内存:


在这个例子中,每个线程分配 2M(1024*1024*2)的内存,并且,占用1s钟不释放。与此同时,其他线程又在马不停蹄的申请内存…所以,内存溢出了。

怎样避免:

这种情况主要出现在多线程系统中,并且每个线程占用内存比较大或者处理速度较慢。

有四种方法来解决这个问题:

  1. 减少处理的线程数
  2. 处理线程需要消耗的内存
  3. 提升线程的处理速度
  4. 增加机器,减少单台机器所需承担的请求量

你可能感兴趣的:(java,java,jdk,sun,tree,string,多线程)