Jvm記憶體泄漏

  • 2019 年 10 月 3 日
  • 筆記

記憶體泄漏和記憶體溢出的關係

記憶體泄露:指程式中動態分配記憶體給一些臨時對象,但是對象不會被GC所回收,它始終佔用記憶體。即被分配的對象可達但已無用。
記憶體溢出:指程式運行過程中無法申請到足夠的記憶體而導致的一種錯誤。記憶體溢出通常發生於OLD段或Perm段垃圾回收後,仍然無記憶體空間容納新的Java對象的情況。
從定義上可以看出記憶體泄露是記憶體溢出的一種誘因,但是不是唯一因素。

可以使用Runtime.getRuntime().freeMemory()進行記憶體泄漏查詢

Runtime.getRuntime().freeMemory()表示當前還有多少空閑記憶體

package com.one.util;    public class Hello {      public static void main(String[] args) {          System.out.println("free記憶體:" + Runtime.getRuntime().freeMemory() / 1024              / 1024);          String[] aaa = new String[2000000];          for (int i = 0; i < 2000000; i++) {              aaa[i] = new String("aaa");          }          System.out.println("free記憶體:" + Runtime.getRuntime().freeMemory() / 1024 / 1024);      }  }

此時結果如下所示

記憶體泄漏的例子

如果長生命周期的對象持有短生命周期的引用,就很可能會出現記憶體泄露

比如下面的程式碼,這裡的object實例,其實我們期望它只作用於method1()方法中,且其他地方不會再用到它,但是,當method1()方法執行完成後,object對象所分配的記憶體不會馬上被認為是可以被釋放的對象,只有在Simple類創建的對象被釋放後才會被釋放,嚴格的說,這就是一種記憶體泄露。

public class Simple {        Object object;        public void method1(){          object = new Object();          //...其他程式碼      }  }    

怎麼解決上面的問題呢,加上下面的藍色程式碼注釋就好了

public class Simple {        Object object;        public void method1(){          object = new Object();          //...其他程式碼          // 藍色程式碼注釋開始          object = null;          // 藍色程式碼注釋結束      }  }

集合裡面的記憶體泄漏

集合裡面的數據都設置成null,但是集合記憶體還是存在的

比如下面的程式碼
因為你已經在下面的藍色程式碼注釋裡面進行company=null了,所以下面的list集合裡面的數據都是無用的了,但是此時list集合裡面的所有元素都不會進行垃圾回收

package com.four;    import java.util.ArrayList;  import java.util.List;    public class Hello {      public static void main(String[] args) {          List<Company> list = new ArrayList<Company>();          int i=0;          for(int j=0;j<10;j++){              Company company = new Company();              company.setName("ali");              list.add(company);              // 藍色程式碼注釋開始              company = null;              // 藍色程式碼注釋結束          }            System.gc();          while(true){              try {                  Thread.sleep(1000);              } catch (InterruptedException e) {                  e.printStackTrace();              }              System.out.println("已經測試了"+(++i)+"秒");          }      }    }      class Company {      private String name;        @Override      protected void finalize() throws Throwable {          super.finalize();          System.out.println("回收Comapny");      }        public void setName(String name) {          this.name = name;      }        public String getName() {          return name;      }  }

怎麼解決上面的問題呢,就是把上面的list集合變數也變成null,比如加上下面的紅色程式碼注釋

package com.one.util;    import java.util.ArrayList;  import java.util.List;    public class Hello {      public static void main(String[] args) {          List<Company> list = new ArrayList<Company>();          int i = 0;          for (int j = 0; j < 10; j++) {              Company company = new Company();              company.setName("ali");              list.add(company);              // 藍色程式碼注釋開始              company = null;              // 藍色程式碼注釋結束          }          // 紅色程式碼注釋開始          list = null;         // 紅色程式碼注釋結束          System.gc();          while (true) {              try {                  Thread.sleep(1000);              } catch (InterruptedException e) {                  e.printStackTrace();              }              System.out.println("已經測試了" + (++i) + "秒");          }      }    }    class Company {      private String name;        @Override      protected void finalize() throws Throwable {          super.finalize();          System.out.println("回收Comapny");      }        public void setName(String name) {          this.name = name;      }        public String getName() {          return name;      }  }  

此時結果如下所示,可以看出來集合裡面的Company變數都回收了

還有就是使用remove()方法進行移除元素的時候,也可能會造成記憶體泄漏

什麼意思呢,
就比如ArrayList裡面的pop(),如果是下面的寫法就會造成記憶體泄漏,因為下面的elementData[–size]這個元素移除之後,並沒有進行設置成null

public E pop(){      if(size == 0)          return null;      else          return (E) elementData[size];  }  

所以上面的程式碼應該變成下面這樣,此時注意下面的藍色程式碼注釋裡面的size值比下面的紅色程式碼注釋裡面的size小1

public E pop(){      if(size == 0)          return null;      else{          // 紅色程式碼注釋開始          E e = (E) elementData[--size];          // 紅色程式碼注釋結束          // 藍色程式碼注釋開始          elementData[size] = null;          // 藍色程式碼注釋結束          return e;      }  }

連接沒有關閉會泄漏

比如資料庫連接(dataSourse.getConnection()),網路連接(socket)和io連接,這些鏈接在使用的時候,除非顯式的調用了其close()方法(或類似方法)將其連接關閉,否則是不會自動被GC回收的。其實原因依然是長生命周期對象持有短生命周期對象的引用。所以我們經常在網上看到在連接調用結束的時候要進行調用close()進行關閉,這樣可以回收不用的記憶體對象,增加可用記憶體。

能看到這裡的同學,就幫忙點個推薦吧吧,Thanks♪(・ω・)ノ

原文鏈接