1. 程式人生 > >Java的記憶體溢位與記憶體洩漏

Java的記憶體溢位與記憶體洩漏

記憶體溢位是啥?記憶體洩漏是啥?它們兩個有關係嗎?讓我們帶著上面的問題來看本篇文章 ······································································································································

基本概念

  1. 記憶體洩漏:是指在程式中動態分配記憶體給一些臨時物件,但是物件生命週期過後,GC不會對它進行回收使它始終佔用記憶體,就容易造成記憶體洩漏。
  2. 記憶體溢位:是指在新的物件生成的時候,在給它分配記憶體的時發現沒有可以分配的記憶體空間,就是記憶體溢位。(記憶體洩漏是造成記憶體溢位的誘因
    之一,並不是唯一)

為什麼要了解記憶體洩露和記憶體溢位

  1. 記憶體洩露一般是程式設計師在編寫和設計程式碼的時候存在缺陷導致的,通過了解記憶體洩露的場景,可以避免不必要的記憶體溢位和提高程式設計師自己的程式碼編寫水平。
  2. 通過了解記憶體溢位的幾種常見情況,可以在出現記憶體溢位的時候快速的定位問題的位置,縮短解決故障的時間。

記憶體洩露的幾種場景:

1、長生命週期的物件持有短生命週期物件的引用

        這是記憶體洩露最常見的場景,也是程式碼設計中經常出現的問題。
        例如:在全域性靜態map中快取區域性變數,且沒有清空操作,隨著時間的推移,這個map會越來越大,造成記憶體洩露。

2、修改hashset中物件的引數值,且引數是計算雜湊值的欄位

         當一個物件被儲存進HashSet集合中以後,就不能修改這個物件中的那些參與計算雜湊值的欄位,否則物件修改後的雜湊值與最初儲存進HashSet集合中時的雜湊值就不同了,在這種情況下,即使在contains方法使用該物件的當前引用作為引數去HashSet集合中檢索物件,也將返回找不到物件的結果,這也會導致無法從HashSet集合中刪除當前物件,造成記憶體洩露。

3、機器的連線數和關閉時間設定

        長時間開啟非常耗費資源的連線,也會造成記憶體洩露。

記憶體溢位的幾種情況:

1、堆記憶體溢位(outOfMemoryError:java heap space)

在jvm規範中,堆中的記憶體是用來生成物件例項和陣列的。 如果細分,堆記憶體還可以分為年輕代和年老代,年輕代包括一個eden區和兩個survivor區。 當生成新物件時,記憶體的申請過程如下: a、jvm先嚐試在eden區分配新建物件所需的記憶體; b、如果記憶體大小足夠,申請結束,否則下一步; c、jvm啟動youngGC,試圖將eden區中不活躍的物件釋放掉,釋放後若Eden空間仍然不足以放入新物件,則試圖將部分Eden中活躍物件放入Survivor區; d、Survivor區被用來作為Eden及old的中間交換區域,當OLD區空間足夠時,Survivor區的物件會被移到Old區,否則會被保留在Survivor區; e、 當OLD區空間不夠時,JVM會在OLD區進行full GC; f、full GC後,若Survivor及OLD區仍然無法存放從Eden複製過來的部分物件,導致JVM無法在Eden區為新物件建立記憶體區域,則出現”out of memory錯誤”: outOfMemoryError:java heap space

程式碼舉例:

Java程式碼

/**

  • 堆記憶體溢位
  • jvm引數:-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m

*/ public class MemoryLeak { private String[] s = new String[1000]; public static void main(String[] args) throws InterruptedException { Map<String,Object> m =new HashMap<String,Object>(); int i =0; int j=10000; while(true){ for(;i<j;i++){ MemoryLeak memoryLeak = new MemoryLeak(); m.put(String.valueOf(i), memoryLeak); } } } }

2、方法區記憶體溢位(outOfMemoryError:permgem space) 在jvm規範中,方法區主要存放的是類資訊、常量、靜態變數等。 所以如果程式載入的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能導致該區發生記憶體溢位,一般該區發生記憶體溢位時的錯誤資訊為: outOfMemoryError:permgem space

程式碼舉例:

Java程式碼

jvm引數:-XX:PermSize=2m -XX:MaxPermSize=2m 將方法區的大小設定很低即可,在啟動載入類庫時就會出現記憶體不足的情況

3、執行緒棧溢位(java.lang.StackOverflowError) 執行緒棧時執行緒獨有的一塊記憶體結構,所以執行緒棧發生問題必定是某個執行緒執行時產生的錯誤。 一般執行緒棧溢位是由於遞迴太深或方法呼叫層級過多導致的。 發生棧溢位的錯誤資訊為: java.lang.StackOverflowError

程式碼舉例:

Java程式碼

/**

  • 執行緒操作棧溢位
  • 引數:-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m -Xss64k

*/ public class StackOverflowTest { public static void main(String[] args) { int i =0; digui(i); } private static void digui(int i){ System.out.println(i++); String[] s = new String[50]; digui(i); } }

為了避免記憶體洩露,在編寫程式碼的過程中可以參考下面的建議:

  1. 儘早釋放無用物件的引用
  2. 使用字串處理,避免使用String,應大量使用StringBuffer,每一個String物件都得獨立佔用記憶體一塊區域
  3. 儘量少用靜態變數,因為靜態變數存放在永久代(方法區),永久代基本不參與垃圾回收
  4. 避免在迴圈中建立物件
  5. 開啟大型檔案或從資料庫一次拿了太多的資料很容易造成記憶體溢位,所以在這些地方要大概計算一下資料量的最大值是多少,並且設定所需最小及最大的記憶體空間值

如有轉載請加本文連結,如有不足望大神指出