最近在看《深刻理解Java虛擬機》,書中給了幾個例子,比較好的說明了幾種OOM(OutOfMemory)產生的過程,大部分的程序員在寫程序時不會太關注Java運行時數據區域的結構: java
感受有必要經過幾個實在的例子來加深對這幾個區域的瞭解 程序員
1)Java堆 windows
全部對象的實例分配都在Java堆上分配內存,堆大小由-Xmx和-Xms來調節,sample以下所示: ide
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()); } } }
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space spa
而且能自動生成Dump。 .net
2)方法區 線程
方法區是存放虛擬機加載類的相關信息,如類、靜態變量和常量,大小由-XX:PermSize和-XX:MaxPermSize來調節,類太多有可能撐爆永久帶: 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(); } } }
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space 對象
靜態變量或常量也會有可能撐爆方法區: blog
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()); } } }
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
3)Java棧和本地方法棧
棧是存放線程調用方法時存儲局部變量表,操做,方法出口等與方法執行相關的信息,棧大小由Xss來調節,方法調用層次太多會撐爆這個區域,samples以下所示:
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; } } }
Exception in thread "main" java.lang.StackOverflowError
打印出Stack length:1007,這裏能夠看出,在個人機器上128k的棧容量能承載深度爲1007的方法調用。固然報這樣的錯不多見,通常只會出現無限循環的遞歸中,另外,線程太多也會佔滿棧區域:
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; } } }
不過在windows上運行這個例子要當心,會出現系統假死的狀況,有可能須要重啓機器才行。
以上幾個例子雖然比較簡單,但能很好幫助普通的程序員更加直觀的瞭解Java堆,方法區,Java棧和本地方法棧。