1. 程式人生 > 實用技巧 >一位面試官詢問我:Java中的JVM記憶體溢位和記憶體洩露是什麼?我這麼回答成功拿到了offer。

一位面試官詢問我:Java中的JVM記憶體溢位和記憶體洩露是什麼?我這麼回答成功拿到了offer。

一位面試官詢問我:Java中的JVM記憶體溢位和記憶體洩露是什麼?我這麼回答成功拿到了offer。

1. 記憶體洩漏(memory leak )

申請了記憶體用完了不釋放,比如一共有 1024M 的記憶體,分配了 521M 的記憶體一直不回收,那麼可以用的記憶體只有 521M 了,彷彿洩露掉了一部分;

通俗一點講的話,記憶體洩漏就是【佔著茅坑不拉shi】。

整理了最新2020整理收集的一線網際網路公司面試真題(都整理成文件),有很多幹貨,包含netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,我感覺在面試這塊講的非常清楚:獲取面試資料只需:

點選這裡領取!!!暗號:CSDN

2. 記憶體溢位(out of memory)

申請記憶體時,沒有足夠的記憶體可以使用;

通俗一點兒講,一個廁所就三個坑,有兩個站著茅坑不走的(記憶體洩漏),剩下最後一個坑,廁所表示接待壓力很大,這時候一下子來了兩個人,坑位(記憶體)就不夠了,記憶體洩漏變成記憶體溢位了。

可見,記憶體洩漏和記憶體溢位的關係:記憶體洩露的增多,最終會導致記憶體溢位。

這是一個很有味道的例子。

如上圖:

物件 X 引用物件 Y,X 的生命週期比 Y 的生命週期長;

那麼當Y生命週期結束的時候,X依然引用著Y,這時候,垃圾回收期是不會回收物件Y的;

如果物件X還引用著生命週期比較短的A、B、C,物件A又引用著物件 a、b、c,這樣就可能造成大量無用的物件不能被回收,進而佔據了記憶體資源,造成記憶體洩漏,直到記憶體溢位。

洩漏的分類

經常發生:發生記憶體洩露的程式碼會被多次執行,每次執行,洩露一塊記憶體;

偶然發生:在某些特定情況下才會發生;

一次性:發生記憶體洩露的方法只會執行一次;

隱式洩露:一直佔著記憶體不釋放,直到執行結束;嚴格的說這個不算記憶體洩露,因為最終釋放掉了,但是如果執行時間特別長,也可能會導致記憶體耗盡。

導致記憶體洩漏的常見原因

  1. 迴圈過多或死迴圈,產生大量物件;

  2. 靜態集合類引起記憶體洩漏,因為靜態集合的生命週期和 JVM 一致,所以靜態集合引用的物件不能被釋放;下面這個例子中,list 是靜態的,只要 JVM 不停,那麼 obj 也一直不會釋放。

public class OOM {
static List list = new ArrayList(); public void oomTests(){ Object obj = new Object(); list.add(obj); } }
  1. 單例模式,和靜態集合導致記憶體洩露的原因類似,因為單例的靜態特性,它的生命週期和 JVM 的生命週期一樣長,所以如果單例物件如果持有外部物件的引用,那麼這個外部物件也不會被回收,那麼就會造成記憶體洩漏。

  2. 資料連線、IO、Socket連線等等,它們必須顯示釋放(用程式碼 close 掉),否則不會被 GC 回收。

try {
 Connection conn = null;
 Class.forName("com.mysql.jdbc.Driver");
 conn = DriverManager.getConnection("url","", "");
 Statement stmt = conn.createStatement() ;
 ResultSet rs = stmt.executeQuery("....") ; 
} catch (Exception e) {
 //異常日誌
} finally {
 //1.關閉結果集 Statement
 //2.關閉宣告的物件 ResultSet
 //3.關閉連線 Connection
}
  1. 內部類的物件被長期持有,那麼內部類物件所屬的外部類物件也不會被回收。

  2. Hash 值發生改變,比如下面中的這個類,它的 hashCode 會隨著變數 x 的變化而變化:

public class ChangeHashCode {
 private int x ;

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + x;
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  ChangeHashCode other = (ChangeHashCode) obj;
  if (x != other.x)
   return false;
  return true;
 }
 //省略 set 、get 方法
}
public class HashSetTests {
 public static void main(String[] args){
  HashSet<ChangeHashCode> hs = new HashSet<ChangeHashCode>();
  
  ChangeHashCode cc = new ChangeHashCode();
  cc.setX(10);//hashCode = 41
  hs.add(cc);
  
  cc.setX(20);//hashCode = 51
  System.out.println("hs.remove = " + hs.remove(cc));//false
  
  hs.add(cc);
  System.out.println("hs.size = " + hs.size());//size = 2
  
 }
}

可以看到,在測試方法中,當元素的 hashCode 發生改變之後,就再也找不到改變之前的那個元素了;

這也是 String 為什麼被設定成了不可變型別,我們可以放心地把 String 存入 HashSet,或者把 String 當做 HashMap 的 key 值;

當我們想把自己定義的類儲存到散列表的時候,需要保證物件的 hashCode 不可變。

  1. 記憶體中載入資料量過大;之前專案在一次上線的時候,應用啟動奇慢直到夯死,就是因為程式碼中會載入一個表中的資料到快取(記憶體)中,測試環境只有幾百條資料,但是生產環境有幾百萬的資料。

最後

最新2020整理收集的一線網際網路公司面試真題(都整理成文件),有很多幹貨,包含netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,我感覺在面試這塊講的非常清楚:獲取面試資料只需:點選這裡領取!!!暗號:CSDN