深刻理解java虛擬機【內存溢出實例】

經過簡單的小例子程序,演示java虛擬機各部份內存溢出狀況:java

(1).java堆溢出:web

Java堆用於存儲實例對象,只要不斷建立對象,而且保證GC Roots到對象之間有引用的可達,避免垃圾收集器回收實例對象,就會在對象數量達到堆最大容量時產生OutOfMemoryError異常。多線程

想要方便快速地產生堆溢出,要使用以下java虛擬機參數:-Xms10m(最小堆內存爲10MB),-Xmx10m(最大堆內存爲10MB,最小堆內存和最大堆內存相同是爲了不堆動態擴展),-XX:+HeapDumpOnOutOfMemoryError可讓java虛擬機在出現內存溢出時產生當前堆內存快照以便進行異常分析。app

例子代碼以下:ide

[java]  view plain copy
 
  1. public class HeapOOM{  
  2.     static class OOMObject{  
  3. }  
  4. public static void main(String[] args){  
  5.     List<OOMObject> list = new ArrayList<OOMObject>();  
  6.     while(true){  
  7.     list.add(new OOMObject());  
  8. }  
  9. }  
  10. }  

 

運行一段時間就會發現產生OutOfMemoryError異常,而且產生了堆內存異常dump文件。oop

(2).java虛擬機棧和本地方法棧溢出:url

因爲Sun的HotSpot虛擬機不區分java虛擬機棧和本地方法棧,所以對於HotSpot虛擬機來講-Xoss參數(設置本地方法棧大小)雖然存在,可是其實是無效的,棧容量只能由-Xss參數設定。spa

因爲Java虛擬機棧會出現StackOverflowError和OutOfMemoryError兩種異常,因此分別使用兩個例子演示這兩種狀況:操作系統

a.java虛擬機棧深度溢出:.net

單線程的環境下,不管是因爲棧幀太大,仍是虛擬機棧容量過小,當內存沒法再分配的時候,虛擬機總拋出StackOverflowError異常。使用-Xss128k將java虛擬機棧大小設置爲128kb,例子代碼以下:

[java]  view plain copy
 
  1. public class JavaVMStackOF{  
  2.     private int stackLength = 1;  
  3.     public void stackLeak(){  
  4.         statckLength++;  
  5.         stackLeak();  
  6. }  
  7. public static void main(String[] args){  
  8.     JavaVMStackOF oom = new JavaVMStackOF();  
  9. oom.stackLeak();  
  10. }  
  11. }  

運行一段時間後,產生StackOverflowError異常。Java虛擬機棧溢出通常會產生在方法遞歸調用過多而java虛擬機棧內存不夠的狀況下。

b.java虛擬機棧內存溢出:

多線程環境下,可以建立的線程最大內存=物理內存-最大堆內存-最大方法區內存,在java虛擬機棧內存必定的狀況下,單個線程佔用的內存越大,所能建立的線程數目越小,因此在多線程條件下很容易產生java虛擬機棧內存溢出的異常。

使用-Xss2m參數設置java虛擬機棧內存大小爲2MB,例子代碼以下:

[java]  view plain copy
 
  1. public class JavaVMStackOOM{  
  2.     private void dontStop(){  
  3.     while(true){  
  4. }  
  5. }  
  6. public void stackLeakByThread{  
  7.     while(true){  
  8.         Thread t = new Thread(new Runnable(){  
  9.     public void run(){  
  10.     dontStop();  
  11. }  
  12. });  
  13. t.start();  
  14. }  
  15. }   
  16. public static void main(String[] args){  
  17.     JavaVMStackOOM oom = new JavaVMStackOOM();  
  18.     oom. stackLeakByThread();.  
  19. }  
  20. }  

 

運行一段時間以後,java虛擬機棧就會由於內存過小沒法建立線程而產生OutOfMemoryError。

(3).運行時常量池溢出:

運行時常量池屬於方法區的一部分,能夠使用-XX:PermSize=10m和-XX:MaxPermSize=10m將永久代最大內存和最小內存設置爲10MB大小,而且因爲永久代最大內存和最小內存大小相同,所以沒法擴展。

String的intern()方法用於檢查常量池中若是有等於此String對象的字符串存在,則直接返回常量池中的字符串對象,不然,將此String對象所包含的字符串添加到運行時常量池中,並返回此String對象的引用。所以String的intern()方法特別適合演示運行時常量池溢出,例子代碼以下:

[java]  view plain copy
 
  1. public class RuntimeConstantPoolOOM{  
  2.     public static void main(String[] args){  
  3. List<String> list = new ArrayList<String>();  
  4.         int i = 0;  
  5.         while(true){  
  6.         list.add(String.valueOf(i++).intern());  
  7. }  
  8. }  
  9. }  

運行一段時間,永久代內存不夠,運行時常量池因沒法再添加常量而產生OutOfMemoryError。

(4).方法區溢出:

運行時常量池是方法區的一部分,他們都屬於HotSpot虛擬機中的永久代內存區域。方法區用於存放Class的相關信息,Java的反射和動態代理能夠動態產生Class,另外第三方的CGLIB能夠直接操做字節碼,也能夠動態產生Class,實驗經過CGLIB來演示,一樣使用-XX:PermSize=10m和-XX:MaxPermSize=10m將永久代最大內存和最小內存設置爲10MB大小,而且因爲永久代最大內存和最小內存大小相同,所以沒法擴展。例子代碼以下:

[java]  view plain copy
 
  1. public class JavaMethodAreaOOM{  
  2.     public static void main(String[] args){  
  3.     while(true){  
  4.     Enhancer enhancer = new Enhancer();  
  5.     enhancer.setSuperClass(OOMObject.class);  
  6.     enhancer.setUseCache(false);  
  7.     enhancer.setCallback(new MethodInterceptor(){  
  8.     public Object intercept(Object obj, Method method, Object[] args,   
  9.                       MethodProxy proxy)throws Throwable{  
  10.     return proxy.invokeSuper(obj, args);  
  11. }  
  12. });  
  13. enhancer.create();  
  14. }  
  15. }  
  16. class OOMObject{  
  17. }   
  18. }  

運行一段時間以後,永久代內存不夠,方法區沒法再存放CGLIB建立處理的Class信息,產生方法區OutOfMemoryError。

(5).本機直接內存溢出:

Java虛擬機能夠經過參數-XX:MaxDirectMemorySize設定本機直接內存可用大小,若是不指定,則默認與java堆內存大小相同。JDK中能夠經過反射獲取Unsafe類(Unsafe的getUnsafe()方法只有啓動類加載器Bootstrap才能返回實例)直接操做本機直接內存。經過使用-XX:MaxDirectMemorySize=10M,限制最大可以使用的本機直接內存大小爲10MB,例子代碼以下:

[java]  view plain copy
 
  1. public class DirectMemoryOOM{  
  2.     private static final int _1MB = 10241024 * 1024;  
  3.     publc static void main(String[] args) throws Exception{  
  4.         Field unsafeField = Unsafe.class.getDeclaredFields()[0];  
  5.         unsafeField.setAccessible(true);  
  6.         Unsafe unsafe = (Unsafe) unsafeField.get(null);  
  7.         while(true){  
  8.             //unsafe直接想操做系統申請內存  
  9.     unsafe.allocateMemory(_1MB);  
  10. }  
  11. }  
  12. }  

當運行一段時間以後,10MB的本機直接內存被分配光,沒法在進行直接內存分配時,產生OutOfMemoryError。

相關文章
相關標籤/搜索