JVM学习之常见溢出类型
Java堆
所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节,sample如下所示:
1 public class HeapOOM { 2 3 static class OOMObject{} 4 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 List<OOMObject> list = new ArrayList<OOMObject>(); 10 11 while(true){ 12 list.add(new OOMObject()); 13 } 14 } 15 16 }
加上JVM参数-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError,就能很快报出OOM:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
并且能自动生成Dump。
方法区)
方法区是存放虚拟机加载类的相关信息,如类、静态变量和常量,大小由-XX:PermSize和-XX:MaxPermSize来调节,类太多有可能撑爆永久带:
1 public class MethodAreaOOM { 2 3 static class OOMOjbect{} 4 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 // TODO Auto-generated method stub 10 while(true){ 11 Enhancer eh = new Enhancer(); 12 eh.setSuperclass(OOMOjbect.class); 13 eh.setUseCache(false); 14 eh.setCallback(new MethodInterceptor(){ 15 16 @Override 17 public Object intercept(Object arg0, Method arg1, 18 Object[] arg2, MethodProxy arg3) throws Throwable { 19 // TODO Auto-generated method stub 20 return arg3.invokeSuper(arg0, arg2); 21 } 22 23 }); 24 eh.create(); 25 } 26 } 27 28 }
加上永久带的JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后会报如下异常:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
静态变量或常量也会有可能撑爆方法区:
1 public class ConstantOOM { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 // TODO Auto-generated method stub 8 List<String> list = new ArrayList<String>(); 9 int i=0; 10 while(true){ 11 list.add(String.valueOf(i++).intern()); 12 } 13 } 14 15 }
同样加上JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后报如下异常:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
3)Java栈和本地方法栈
栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,栈大小由Xss来调节,方法调用层次太多会撑爆这个区域,samples如下所示:
1 package com.cutesource; 2 3 public class StackOOM { 4 5 /** 6 * @param args 7 */ 8 9 private int stackLength = 1; 10 11 public void stackLeak(){ 12 stackLength++; 13 stackLeak(); 14 } 15 16 public static void main(String[] args) throws Throwable{ 17 // TODO Auto-generated method stub 18 StackOOM oom = new StackOOM(); 19 try{ 20 oom.stackLeak(); 21 }catch(Throwable err){ 22 System.out.println("Stack length:" + oom.stackLength); 23 throw err; 24 } 25 26 } 27 28 }
设置JVM参数:-Xss128k,报出异常:
Exception in thread "main" java.lang.StackOverflowError
打印出Stack length:1007,这里可以看出,在我的机器上128k的栈容量能承载深度为1007的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:
1 package com.cutesource; 2 3 public class StackOOM { 4 5 /** 6 * @param args 7 */ 8 9 private int stackLength = 1; 10 11 private void dontStop(){ 12 while(true){ 13 try{Thread.sleep(1000);}catch(Exception err){} 14 } 15 } 16 17 public void stackLeakByThread(){ 18 while(true){ 19 Thread t = new Thread(new Runnable(){ 20 21 @Override 22 public void run() { 23 // TODO Auto-generated method stub 24 dontStop(); 25 } 26 27 }); 28 t.start(); 29 stackLength++; 30 } 31 } 32 33 public static void main(String[] args) throws Throwable{ 34 // TODO Auto-generated method stub 35 StackOOM oom = new StackOOM(); 36 try{ 37 oom.stackLeakByThread(); 38 }catch(Throwable err){ 39 System.out.println("Stack length:" + oom.stackLength); 40 throw err; 41 } 42 43 } 44 45 }
报出异常:Exception in thread "main" java.lang.OutOfMemoryError:unable to create new native thread
不过在windows上运行这个例子要小心,会出现系统假死的情况,有可能需要重启机器才行。
以上几个例子虽然比较简单,但能很好帮助普通的程序员更加直观的了解Java堆,方法区,Java栈和本地方法栈。