下面也来看一下上面说到的几个内存模块导致的内存溢出异常问题:

(这个也是面试的时候经常会被问到:比如叫你写一段让堆内存溢出的代码,或者是问你如果如果修改堆大小)

第一、堆溢出
[java] view plaincopy在CODE上查看代码片派生到我的代码片

public class HeapOOM {  
      
    static class OOMObject{}  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        List<OOMObject> list = new ArrayList<OOMObject>();  
          
        while(true){  
            list.add(new OOMObject());  
        }  
    }  
  
}  

我们上面看到堆主要是存放对象的,所以我们如果想让堆出现OOM的话,可以开一个死循环,然后产生新的对象就可以了。然后在将堆的大小调小点。

加上JVM参数

-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError,

就能很快报出OOM:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

第二、栈溢出
[java] view plaincopy在CODE上查看代码片派生到我的代码片

package com.cutesource;  
  
public class StackOOM {  
  
    /** 
     * @param args 
     */  
      
    private int stackLength = 1;  
      
    public void stackLeak(){  
        stackLength++;  
        stackLeak();  
    }  
      
    public static void main(String[] args) throws Throwable{  
        // TODO Auto-generated method stub  
        StackOOM oom = new StackOOM();  
        try{  
            oom.stackLeak();  
        }catch(Throwable err){  
            System.out.println("Stack length:" + oom.stackLength);  
            throw err;  
        }  
          
    }  
  
}  

我们知道栈中存放的方法执行的过程中需要的空间,所以我们可以下一个循环递归,这样方法栈就会出现OOM的异常了。

设置JVM参数:-Xss128k,报出异常:

Exception in thread "main" java.lang.StackOverflowError

打印出Stack length:1007,这里可以看出,在我的机器上128k的栈容量能承载深度为1007的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:
[java] view plaincopy在CODE上查看代码片派生到我的代码片

package com.cutesource;  
  
public class StackOOM {  
  
    /** 
     * @param args 
     */  
      
    private int stackLength = 1;  
      
    private void dontStop(){  
        while(true){  
            try{Thread.sleep(1000);}catch(Exception err){}  
        }  
    }  
      
    public void stackLeakByThread(){  
        while(true){  
            Thread t = new Thread(new Runnable(){  
  
                @Override  
                public void run() {  
                    // TODO Auto-generated method stub  
                    dontStop();  
                }  
                  
            });  
            t.start();  
            stackLength++;  
        }  
    }  
      
    public static void main(String[] args) throws Throwable{  
        // TODO Auto-generated method stub  
        StackOOM oom = new StackOOM();  
        try{  
            oom.stackLeakByThread();  
        }catch(Throwable err){  
            System.out.println("Stack length:" + oom.stackLength);  
            throw err;  
        }  
          
    }  
  
}  

这个栈的溢出,就是我们上面说到栈的时候的两种异常情况。

报出异常:Exception in thread "main" java.lang.OutOfMemoryError:unable to create new native thread

第三、方法区溢出
[java] view plaincopy在CODE上查看代码片派生到我的代码片

public class MethodAreaOOM {  
      
    static class OOMOjbect{}  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        while(true){  
            Enhancer eh = new Enhancer();  
            eh.setSuperclass(OOMOjbect.class);  
            eh.setUseCache(false);  
            eh.setCallback(new MethodInterceptor(){  
  
                @Override  
                public Object intercept(Object arg0, Method arg1,  
                        Object[] arg2, MethodProxy arg3) throws Throwable {  
                    // TODO Auto-generated method stub  
                    return arg3.invokeSuper(arg0, arg2);  
                }  
                  
            });  
            eh.create();  
        }  
    }  
  
}  

我们知道方法区是存放一些类的信息等,所以我们可以使用类加载无限循环加载class,这样就会出现方法区的OOM异常。

手动将栈的大小调小点

加上JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后会报如下异常:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

第四、常量池溢出
[java] view plaincopy在CODE上查看代码片派生到我的代码片

public class ConstantOOM {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        List<String> list = new ArrayList<String>();  
        int i=0;  
        while(true){  
            list.add(String.valueOf(i++).intern());  
        }  
    }  
  
}  

我们知道常量池中存放的是运行过程中的常量,同时我们知道String类型的intern方法是将字符串的值放到常量池中的。所以上面弄可以开一个死循环将字符串的值都放到常量池中,这样常量池就会出现OOM异常了。因为常量池本身就是方法区的一部分,所以我们也可以手动的调节一下栈的大小。