【Java】幾種典型的內存溢出案例,都在這兒了!

【Java】幾種典型的內存溢出案例,都在這兒了!

寫在前面
作爲程序員,多多少少都會遇到一些內存溢出的場景,如果你還沒遇到,說明你工作的年限可能比較短,或者你根本就是個假程序員!哈哈,開個玩笑。今天,我們就以Java代碼的方式來列舉幾個典型的內存溢出案例,希望大家在日常工作中,儘量避免寫這些low水平的代碼。

定義主類結構
首先,我們創建一個名稱爲BlowUpJVM的類,之後所有的案例實驗都是基於這個類進行。如下所示。

public class BlowUpJVM {
}
棧深度溢出
public static void testStackOverFlow(){

  BlowUpJVM.testStackOverFlow(); 

}
棧不斷遞歸,而且沒有處理,所以虛擬機棧就不斷深入不斷深入,棧深度就這樣溢出了。

永久代內存溢出
public static void testPergemOutOfMemory1(){
//方法一失敗
List list = new ArrayList();
while(true){

  list.add(UUID.randomUUID().toString().intern()); 

}
}
打算把String常量池堆滿,沒想到失敗了,JDK1.7後常量池放到了堆裏,也能進行垃圾回收了。

然後換種方式,使用cglib,用Class把老年代取堆滿

public static void testPergemOutOfMemory2(){
try {

  while (true) { 
     Enhancer enhancer = new Enhancer(); 
     enhancer.setSuperclass(OOM.class); 
     enhancer.setUseCache(false); 
     enhancer.setCallback(new MethodInterceptor() { 
        @Override 
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
           return proxy.invokeSuper(obj, args); 
        } 
     }); 
     enhancer.create(); 
  } 

}
catch (Exception e){

  e.printStackTrace(); 

}
}
虛擬機成功內存溢出了,那JDK動態代理產生的類能不能溢出呢?

public static void testPergemOutOfMemory3(){
while(true){
final OOM oom = new OOM();
Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        Object result = method.invoke(oom, args); 
        return result; 
     } 
  }); 

}
}
事實表明,JDK動態代理差生的類不會造成內存溢出,原因是:JDK動態代理產生的類信息,不會放到永久代中,而是放在堆中。

本地方法棧溢出
public static void testNativeMethodOutOfMemory(){
int j = 0;
while(true){

  Printer.println(j++); 
  ExecutorService executors = Executors.newFixedThreadPool(50); 
  int i=0; 
  while(i++<10){ 
     executors.submit(new Runnable() { 
        public void run() { 
        } 
     }); 
  } 

}
}
這個的原理就是不斷創建線程池,而每個線程池都創建10個線程,這些線程池都是在本地方法區的,久而久之,本地方法區就溢出了。

JVM棧內存溢出
public static void testStackOutOfMemory(){

while (true) {   
        Thread thread = new Thread(new Runnable() {   
               public void run() { 
                      while(true){ 
                  } 
               }   
        });   
        thread.start();   
 }   

}
線程的創建會直接在JVM棧中創建,但是本例子中,沒看到內存溢出,主機先掛了,不是JVM掛了,真的是主機掛了,無論在mac還是在windows,都掛了。

溫馨提示,這個真的會死機的。

堆溢出
public static void testOutOfHeapMemory(){
List list = new ArrayList();
while(true){

  StringBuffer B = new StringBuffer(); 
  for(int i = 0 ; i < 10000 ; i++){ 
     B.append(i); 
  } 
  list.add(B); 

}
}
不斷往堆中塞新增的StringBuffer對象,堆滿了就直接溢出了。

測試案例完整代碼
public class BlowUpJVM {

//棧深度溢出
public static void  testStackOverFlow(){ 
      BlowUpJVM.testStackOverFlow(); 
} 

//不能引起永久代溢出
public static void testPergemOutOfMemory1(){ 
   //方法一失敗 
    List<String> list = new ArrayList<String>(); 
   while(true){ 
      list.add(UUID.randomUUID().toString().intern()); 
   } 
} 

//永久代溢出
public static void testPergemOutOfMemory2(){ 
   try { 
      while (true) { 
         Enhancer enhancer = new Enhancer(); 
         enhancer.setSuperclass(OOM.class); 
         enhancer.setUseCache(false); 
         enhancer.setCallback(new MethodInterceptor() { 
            @Override 
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
               return proxy.invokeSuper(obj, args); 
            } 
         }); 
         enhancer.create(); 
      } 
   } 
   catch (Exception e){ 
      e.printStackTrace(); 
   } 
} 

//不會引起永久代溢出
public static void testPergemOutOfMemory3(){ 
   while(true){ 
   final OOM oom = new OOM(); 
   Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() { 
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
            Object result = method.invoke(oom, args); 
            return result; 
         } 
      }); 
   } 
} 

//本地方法棧溢出
public static void testNativeMethodOutOfMemory(){ 
   int j = 0; 
   while(true){ 
      Printer.println(j++); 
      ExecutorService executors = Executors.newFixedThreadPool(50); 
      int i=0; 
      while(i++<10){ 
         executors.submit(new Runnable() { 
            public void run() { 
            } 
         }); 
      } 
   } 
} 

//JVM內存溢出
public static void testStackOutOfMemory(){ 
    while (true) {   
            Thread thread = new Thread(new Runnable() {   
                   public void run() { 
                          while(true){ 
                      } 
                   }   
            });   
            thread.start();   
     }   
} 

//堆溢出
public static void testOutOfHeapMemory(){ 
   List<StringBuffer> list = new ArrayList<StringBuffer>(); 
   while(true){ 
      StringBuffer B = new StringBuffer(); 
      for(int i = 0 ; i < 10000 ; i++){ 
         B.append(i); 
      } 
      list.add(B); 
   } 
} 

}
寫在最後
如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習高併發編程技術。

最後,附上併發編程需要掌握的核心技能知識圖,祝大家在學習併發編程時,少走彎路。

原文地址https://www.cnblogs.com/binghe001/p/12952772.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章