1. 程式人生 > >深入理解記憶體溢位和洩露

深入理解記憶體溢位和洩露

上週考試見老師給同學們出的一道題:什麼是記憶體洩露,舉例說明。很有趣的是聽改卷的老師說,前面有一道遞迴題,然後有部分同學直接去掉了遞迴的出口,把他作為記憶體洩露的例子。

這一點讓我意識到很多人其實並沒有分清楚記憶體洩露和記憶體溢位,那麼說根本就是沒懂記憶體洩露和記憶體溢位的根本概念。所以我專門寫了這篇文章,希望通過通俗易懂的語言讓大家對這二者有個清楚的區分。

那麼什麼是記憶體洩漏呢,記憶體洩漏,memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光,這裡需要說一下記憶體洩露的最終會導致記憶體溢位。

java導致記憶體洩露的原因很明確:長生命週期的物件持有短生命週期物件的引用就很可能發生記憶體洩露,儘管短生命週期物件已經不再需要,但是因為長生命週期物件持有它的引用而導致不能被回收,這就是java中記憶體洩露的發生場景。

舉例如下:不正確使用單例模式是引起記憶體洩露的一個常見問題,單例物件在被初始化後將在JVM的整個生命週期中存在(以靜態變數的方式),如果單例物件持有外部物件的引用,那麼這個外部物件將不能被jvm正常回收,導致記憶體洩露。

class A{
  public A(){
    B.getInstance().setA(this);
  }
  }
  //B類採用單例模式
  class B{
  private A a;
  private static B instance=new B();
  public B(){}
  public static B getInstance(){
     return instance;
  }

  public void setA(A a){
  this.a=a;
  }
}

而記憶體溢位呢,記憶體溢位,out of memory。總結的話就是你要求分配的記憶體超出了系統能給你的,系統不能滿足需求,於是產生溢位。比如申請了一個float,但給它存了double才能存下的數,那就會造成記憶體溢位。

這個舉例的話很多比如

while(true){

    int i = 1;

}

直接死迴圈導致一直申請空間最後空間不滿足,就會溢位,亦或者遞迴的時候沒有出口都會導致這個現象。很明顯這樣下去最終會導致程式崩潰。

那麼面試的時候通常面試官會這麼問你:什麼是記憶體洩漏,有什麼危害,舉例說明,並說一下怎麼解決。作為一個高效的開發人員,我們不管是在開發中還是在生活中,要做到不僅找到問題,還能解決問題,這樣才能體現一個人的價值所在。

那麼怎麼去解決記憶體洩露或者記憶體溢位呢,這個之前有一篇記憶體洩露的文章裡面寫的比較全,這裡就引用過來:

1、儘早釋放無用物件的引用。好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後,自動設定為null,暗示垃圾收集器來收集該物件,防止發生記憶體洩露。

2、我們的程式裡不可避免大量使用字串處理,避免使用String,應大量使用StringBuffer,每一個String物件都得獨立佔用記憶體一塊區域

3、儘量少用靜態變數,因為靜態變數是全域性的,GC不會回收的;

4、避免集中建立物件尤其是大物件,JVM會突然需要大量記憶體,這時必然會觸發GC優化系統記憶體環境;

5、儘量運用物件池技術以提高系統性能;生命週期長的物件擁有生命週期短的物件時容易引發記憶體洩漏,例如大集合物件擁有大資料量的業務物件的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。

6、不要在經常呼叫的方法中建立物件,尤其是忌諱在迴圈中建立物件。可以適當的使用hashtable,vector 建立一組物件容器,然後從容器中去取那些物件,而不用每次new之後又丟棄

7、一般都是發生在開啟大型檔案或跟資料庫一次拿了太多的資料,造成 Out Of Memory Error 的狀況,這時就大概要計算一下資料量的最大值是多少,並且設定所需最小及最大的記憶體空間值

總結:大家真正開發中,一定要避免記憶體洩露的問題,它就像一個癌細胞,等到擴散的時候就不好了,保持良好的邏輯和思維去程式設計是必不可少的,另外這兩個概念大家一定要掌握了,一兩年工作經驗的出去面試的時候面試官應該比較喜歡問這個問題。