【效能優化】納尼?記憶體又溢位了?!是時候總結一波了!!

冰河團隊發表於2020-11-05

寫在前面

相信小夥伴們在平時工作的過程中,或多或少都會遇到一個場景:記憶體溢位。如果你沒有遇到過這個場景,那就說明你是個假的程式設計師。哈哈,開個玩笑,平時工作過程中,我們確實會遇到這個問題。今天,我就將平時工作過程中遇到的記憶體溢位情況做個簡單的總結,以通俗易懂的程式碼案例的形式直觀的分享給大家。希望能夠為小夥伴們帶來實質性的幫助。

案例介紹

這裡,我將在平時工作過程中總結的記憶體溢位的情況,以程式碼案例的形式直觀的分享給大家,希望能夠為小夥伴們帶來實質性的幫助。

接下來,我們就以程式碼案例的形式來分析各種記憶體溢位的情況。

定義主類結構

首先,我們建立一個類叫做BlowUpJVM,所有的案例實驗都是基於這個類進行。

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()); 
   } 
} 

打算把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<StringBuffer> list = new ArrayList<StringBuffer>(); 
   while(true){ 
      StringBuffer B = new StringBuffer(); 
      for(int i = 0 ; i < 10000 ; i++){ 
         B.append(i); 
      } 
      list.add(B); 
   } 
} 

不斷往堆中塞新增的StringBuffer物件,堆滿了就直接溢位了。

重磅福利

微信搜一搜【冰河技術】微信公眾號,關注這個有深度的程式設計師,每天閱讀超硬核技術乾貨,公眾號內回覆【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文件,以及我為大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的工作,學習是一條時而鬱鬱寡歡,時而開懷大笑的路,加油。如果你通過努力成功進入到了心儀的公司,一定不要懈怠放鬆,職場成長和新技術學習一樣,不進則退。如果有幸我們江湖再見!

另外,我開源的各個PDF,後續我都會持續更新和維護,感謝大家長期以來對冰河的支援!!

相關文章