qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          Java內(nèi)存泄露的理解與解決

           Java內(nèi)存管理機制

            在C++ 語言中,如果需要動態(tài)分配一塊內(nèi)存,程序員需要負(fù)責(zé)這塊內(nèi)存的整個生命周期。從申請分配、到使用、再到最后的釋放。這樣的過程非常靈活,但是卻十分繁瑣,程序員很容易由于疏忽而忘記釋放內(nèi)存,從而導(dǎo)致內(nèi)存的泄露。 Java 語言對內(nèi)存管理做了自己的優(yōu)化,這就是垃圾回收機制。 Java 的幾乎所有內(nèi)存對象都是在堆內(nèi)存上分配(基本數(shù)據(jù)類型除外),然后由 GC ( garbage collection)負(fù)責(zé)自動回收不再使用的內(nèi)存。

            上面是Java 內(nèi)存管理機制的基本情況。但是如果僅僅理解到這里,我們在實際的項目開發(fā)中仍然會遇到內(nèi)存泄漏的問題。也許有人表示懷疑,既然 Java 的垃圾回收機制能夠自動的回收內(nèi)存,怎么還會出現(xiàn)內(nèi)存泄漏的情況呢?這個問題,我們需要知道 GC 在什么時候回收內(nèi)存對象,什么樣的內(nèi)存對象會被 GC 認(rèn)為是“不再使用”的。

            Java中對內(nèi)存對象的訪問,使用的是引用的方式。在 Java 代碼中我們維護一個內(nèi)存對象的引用變量,通過這個引用變量的值,我們可以訪問到對應(yīng)的內(nèi)存地址中的內(nèi)存對象空間。在 Java 程序中,這個引用變量本身既可以存放堆內(nèi)存中,又可以放在代碼棧的內(nèi)存中(與基本數(shù)據(jù)類型相同)。 GC 線程會從代碼棧中的引用變量開始跟蹤,從而判定哪些內(nèi)存是正在使用的。如果 GC 線程通過這種方式,無法跟蹤到某一塊堆內(nèi)存,那么 GC 就認(rèn)為這塊內(nèi)存將不再使用了(因為代碼中已經(jīng)無法訪問這塊內(nèi)存了)。

            通過這種有向圖的內(nèi)存管理方式,當(dāng)一個內(nèi)存對象失去了所有的引用之后,GC 就可以將其回收。反過來說,如果這個對象還存在引用,那么它將不會被 GC 回收,哪怕是 Java 虛擬機拋出 OutOfMemoryError 。

            Java內(nèi)存泄露

            一般來說內(nèi)存泄漏有兩種情況。一種情況如在C/C++ 語言中的,在堆中的分配的內(nèi)存,在沒有將其釋放掉的時候,就將所有能訪問這塊內(nèi)存的方式都刪掉(如指針重新賦值);另一種情況則是在內(nèi)存對象明明已經(jīng)不需要的時候,還仍然保留著這塊內(nèi)存和它的訪問方式(引用)。第一種情況,在 Java 中已經(jīng)由于垃圾回收機制的引入,得到了很好的解決。所以, Java 中的內(nèi)存泄漏,主要指的是第二種情況。

            可能光說概念太抽象了,大家可以看一下這樣的例子:

          1. Vector v = new  Vector( 10 );  
          2. for  ( int  i = 1 ;i < 100 ; i ++ ){  
          3. Object o = new  Object();  
          4. v.add(o);  
          5. o = null ;  
          6. }

            在這個例子中,代碼棧中存在Vector 對象的引用 v 和 Object 對象的引用 o 。在 For 循環(huán)中,我們不斷的生成新的對象,然后將其添加到 Vector 對象中,之后將 o 引用置空。問題是當(dāng) o 引用被置空后,如果發(fā)生 GC ,我們創(chuàng)建的 Object 對象是否能夠被 GC 回收呢?答案是否定的。因為, GC 在跟蹤代碼棧中的引用時,會發(fā)現(xiàn) v 引用,而繼續(xù)往下跟蹤,就會發(fā)現(xiàn) v 引用指向的內(nèi)存空間中又存在指向 Object 對象的引用。也就是說盡管 o 引用已經(jīng)被置空,但是 Object 對象仍然存在其他的引用,是可以被訪問到的,所以 GC 無法將其釋放掉。如果在此循環(huán)之后, Object 對象對程序已經(jīng)沒有任何作用,那么我們就認(rèn)為此 Java 程序發(fā)生了內(nèi)存泄漏。

            盡管對于C/C++ 中的內(nèi)存泄露情況來說, Java 內(nèi)存泄露導(dǎo)致的破壞性小,除了少數(shù)情況會出現(xiàn)程序崩潰的情況外,大多數(shù)情況下程序仍然能正常運行。但是,在移動設(shè)備對于內(nèi)存和 CPU 都有較嚴(yán)格的限制的情況下, Java 的內(nèi)存溢出會導(dǎo)致程序效率低下、占用大量不需要的內(nèi)存等問題。這將導(dǎo)致整個機器性能變差,嚴(yán)重的也會引起拋出 OutOfMemoryError ,導(dǎo)致程序崩潰。

            一般情況下內(nèi)存泄漏的避免

            在不涉及復(fù)雜數(shù)據(jù)結(jié)構(gòu)的一般情況下,Java 的內(nèi)存泄露表現(xiàn)為一個內(nèi)存對象的生命周期超出了程序需要它的時間長度。我們有時也將其稱為“對象游離”。

            例如:

          1. public class FileSearch{  
          2.       private byte [] content;  
          3.       privateFile mFile;  
          4.      public FileSearch(File file){  
          5.       mFile = file;  
          6.       }  
          7.      public boolean hasString(String str){  
          8.          int size = getFileSize(mFile);  
          9.         content =  new  byte [size];  
          10.          loadFile(mFile, content);  
          11.          String s =  new String(content);  
          12.          return s.contains(str);  
          13.      }  
          14. }

          posted on 2011-12-16 14:20 順其自然EVO 閱讀(193) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2011年12月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 东光县| 汶上县| 鄂尔多斯市| 胶州市| 乐至县| 哈尔滨市| 临清市| 鹤庆县| 越西县| 伊通| 商丘市| 吉首市| 阿拉善右旗| 永德县| 北京市| 达拉特旗| 新安县| 广州市| 夹江县| 大冶市| 安远县| 武清区| 永善县| 青冈县| 武乡县| 平果县| 龙门县| 德化县| 巩留县| 沾益县| 上犹县| 文山县| 娱乐| 淅川县| 宜良县| 康乐县| 手游| 秀山| 商都县| 固镇县| 惠东县|