亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

理解Java中的內存泄露及解決方法示例

2019-11-26 15:15:41
字體:
來源:轉載
供稿:網友

本文詳細地介紹了Java內存管理的原理,以及內存泄露產生的原因,同時提供了一些列解決Java內存泄露的方案,希望對各位Java開發者有所幫助。

Java內存管理機制

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

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

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

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

Java內存泄露

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

復制代碼 代碼如下:

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

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

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

一般情況下內存泄漏的避免

在不涉及復雜數據結構的一般情況下,Java 的內存泄露表現為一個內存對象的生命周期超出了程序需要它的時間長度。我們有時也將其稱為“對象游離”。

例如:

復制代碼 代碼如下:

public class FileSearch{ 
      private byte [] content; 
      private File mFile; 
     public FileSearch(File file){ 
      mFile = file; 
      } 
     public boolean hasString(String str){ 
         int size = getFileSize(mFile); 
        content =  new  byte [size]; 
         loadFile(mFile, content); 
         String s =  new String(content); 
         return s.contains(str); 
     } 
}

在這段代碼中,FileSearch 類中有一個函數 hasString ,用來判斷文檔中是否含有指定的字符串。流程是先將mFile 加載到內存中,然后進行判斷。但是,這里的問題是,將 content 聲明為了實例變量,而不是本地變量。于是,在此函數返回之后,內存中仍然存在整個文件的數據。而很明顯,這些數據我們后續是不再需要的,這就造成了內存的無故浪費。

要避免這種情況下的內存泄露,要求我們以C/C++ 的內存管理思維來管理自己分配的內存。第一,是在聲明對象引用之前,明確內存對象的有效作用域。在一個函數內有效的內存對象,應該聲明為 local 變量,與類實例生命周期相同的要聲明為實例變量……以此類推。第二,在內存對象不再需要時,記得手動將其引用置空。

復雜數據結構中的內存泄露問題

在實際的項目中,我們經常用到一些較為復雜的數據結構用于緩存程序運行過程中需要的數據信息。有時,由于數據結構過于復雜,或者我們存在一些特殊的需求(例如,在內存允許的情況下,盡可能多的緩存信息來提高程序的運行速度等情況),我們很難對數據結構中數據的生命周期作出明確的界定。這個時候,我們可以使用Java 中一種特殊的機制來達到防止內存泄露的目的。

之前我們介紹過,Java 的 GC 機制是建立在跟蹤內存的引用機制上的。而在此之前,我們所使用的引用都只是定義一個“ Object o; ”這樣形式的。事實上,這只是 Java 引用機制中的一種默認情況,除此之外,還有其他的一些引用方式。通過使用這些特殊的引用機制,配合 GC 機制,就可以達到一些我們需要的效果。

Java中的幾種引用方式

Java中有幾種不同的引用方式,它們分別是:強引用、軟引用、弱引用和虛引用。下面,我們首先詳細地了解下這幾種引用方式的意義。

強引用

在此之前我們介紹的內容中所使用的引用 都是強引用,這是使用最普遍的引用。如果一個對象具有強引用,那就類似于必不可少的生活用品,垃圾回收器絕不會回收它。當內存空 間不足,Java 虛擬機寧愿拋出 OutOfMemoryError 錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。

軟引用(SoftReference )

SoftReference 類的一個典型用途就是用于內存敏感的高速緩存。 SoftReference  的原理是:在保持對對象的引用時保證在  JVM  報告內存不足情況之前將清除所有的軟引用。關鍵之處在于,垃圾收集器在運行時可能會(也可能不會)釋放軟可及對象。對象是否被釋放取決于垃圾收集器的算法 以及垃圾收集器運行時可用的內存數量。

弱引用(WeakReference )

WeakReference 類的一個典型用途就是規范化映射( canonicalized mapping )。另外,對于那些生存期相對較長而且重新創建的開銷也不高的對象來說,弱引用也比較有用。關鍵之處在于,垃圾收集器運行時如果碰到了弱可及對象,將釋放  WeakReference  引用的對象。然而,請注意,垃圾收集器可能要運行多次才能找到并釋放弱可及對象。

虛引用(PhantomReference )

PhantomReference 類只能用于跟蹤對被引用對象即將進行的收集。同樣,它還能用于執行  pre-mortem  清除操作。 PhantomReference  必須與  ReferenceQueue  類一起使用。需要  ReferenceQueue  是因為它能夠充當通知機制。當垃圾收集器確定了某個對象是虛可及對象時, PhantomReference  對象就被放在它的  ReferenceQueue  上。將  PhantomReference  對象放在  ReferenceQueue  上也就是一個通知,表明  PhantomReference  對象引用的對象已經結束,可供收集了。這使您能夠剛好在對象占用的內存被回收之前采取行動。 Reference與 ReferenceQueue 的配合使用。

GC、 Reference 與 ReferenceQueue 的交互

A、  GC無法刪除存在強引用的對象的內存。
B、  GC發現一個只有軟引用的對象內存,那么:
①  SoftReference對象的 referent  域被設置為 null ,從而使該對象不再引用 heap 對象。
②  SoftReference引用過的 heap 對象被聲明為 finalizable 。
③  當 heap  對象的  finalize()  方法被運行而且該對象占用的內存被釋放, SoftReference  對象就被添加到它的  ReferenceQueue (如果后者存在的話)。
C、  GC發現一個只有弱引用的對象內存,那么:
①  WeakReference對象的 referent 域被設置為 null , 從而使該對象不再引用heap 對象。
②  WeakReference引用過的 heap 對象被聲明為 finalizable 。
③  當heap 對象的 finalize() 方法被運行而且該對象占用的內存被釋放時, WeakReference 對象就被添加到它的 ReferenceQueue (如果后者存在的話)。
D、  GC發現一個只有虛引用的對象內存,那么:
①  PhantomReference引用過的 heap 對象被聲明為 finalizable 。
②  PhantomReference在堆對象被釋放之前就被添加到它的 ReferenceQueue 。
值得注意的地方有以下幾點:
1、 GC 在一般情況下不會發現軟引用的內存對象,只有在內存明顯不足的時候才會發現并釋放軟引用對象的內存。
2、 GC 對弱引用的發現和釋放也不是立即的,有時需要重復幾次 GC ,才會發現并釋放弱引用的內存對象。
3、軟引用和弱引用在添加到 ReferenceQueue 的時候,其指向真實內存的引用已經被置為空了,相關的內存也已經被釋放掉了。而虛引用在添加到 ReferenceQueue 的時候,內存還沒有釋放,仍然可以對其進行訪問。
代碼示例
通過以上的介紹,相信您對Java 的引用機制以及幾種引用方式的異同已經有了一定了解。光是概念,可能過于抽象,下面我們通過一個例子來演示如何在代碼中使用 Reference 機制。

復制代碼 代碼如下:

String str  =   new  String( " hello " );  // ①  
ReferenceQueue < String >  rq  =   new  ReferenceQueue < String > ();  // ②  
WeakReference < String >  wf  =   new  WeakReference < String > (str, rq);  // ③  
str = null ;  // ④取消"hello"對象的強引用  
String str1 = wf.get();  // ⑤假如"hello"對象沒有被回收,str1引用"hello"對象 
// 假如"hello"對象沒有被回收,rq.poll()返回null  
Reference <?   extends  String >  ref = rq.poll();  // ⑥

在以上代碼中,注意⑤⑥兩處地方。假如“hello ”對象沒有被回收 wf.get() 將返回“ hello ”字符串對象, rq.poll() 返回 null ;而加入“ hello ”對象已經被回收了,那么 wf.get() 返回 null , rq.poll() 返回 Reference 對象,但是此 Reference 對象中已經沒有 str 對象的引用了 ( PhantomReference 則與WeakReference 、 SoftReference 不同 )。

引用機制與復雜數據結構的聯合應用

了解了GC 機制、引用機制,并配合上 ReferenceQueue ,我們就可以實現一些防止內存溢出的復雜數據類型。

例如,SoftReference 具有構建 Cache 系統的特質,因此我們可以結合哈希表實現一個簡單的緩存系統。這樣既能保證能夠盡可能多的緩存信息,又可以保證 Java 虛擬機不會因為內存泄露而拋出 OutOfMemoryError 。這種緩存機制特別適合于內存對象生命周期長,且生成內存對象的耗時比較長的情況,例如緩存列表封面圖片等。對于一些生命周期較長,但是生成內存對象開銷不大的情況,使用WeakReference 能夠達到更好的內存管理的效果。

附SoftHashmap 的源碼一份,相信看過之后,大家會對 Reference 機制的應用有更深入的理解。

復制代碼 代碼如下:

package  com. *** .widget; 
    // : SoftHashMap.java   
    import  java.util. * ;  
    import  java.lang.ref. * ;  
    import  android.util.Log; 

    public   class  SoftHashMap  extends  AbstractMap  {  
      /**  The internal HashMap that will hold the SoftReference.  */   
      private   final  Map hash  =   new  HashMap();  
      /**  The number of "hard" references to hold internally.  */   
      private   final   int  HARD_SIZE;  
      /**  The FIFO list of hard references, order of last access.  */   
      private   final  LinkedList hardCache  =   new  LinkedList();  
      /**  Reference queue for cleared SoftReference objects.  */   
      private  ReferenceQueue queue  =   new  ReferenceQueue();  
      // Strong Reference number  
      public  SoftHashMap()  {  this ( 100 ); }   
      public  SoftHashMap( int  hardSize)  { HARD_SIZE  =  hardSize; }   

      public  Object get(Object key)  {  
       Object result  =   null ;  
        //  We get the SoftReference represented by that key   
       SoftReference soft_ref  =  (SoftReference)hash.get(key);  
        if  (soft_ref  !=   null )  {  
          //  From the SoftReference we get the value, which can be  
          //  null if it was not in the map, or it was removed in  
          //  the processQueue() method defined below   
        result  =  soft_ref.get();  
          if  (result  ==   null )  {  
            //  If the value has been garbage collected, remove the  
            //  entry from the HashMap.   
           hash.remove(key);  
         }   else   {  
            //  We now add this object to the beginning of the hard  
            //  reference queue.  One reference can occur more than  
            //  once, because lookups of the FIFO queue are slow, so  
            //  we don't want to search through it each time to remove  
            //  duplicates.  
              // keep recent use object in memory  
           hardCache.addFirst(result);  
            if  (hardCache.size()  >  HARD_SIZE)  {  
              //  Remove the last entry if list longer than HARD_SIZE   
             hardCache.removeLast();  
           }   
         }   
       }   
        return  result;  
     }   

      /**  We define our own subclass of SoftReference which contains  
      not only the value but also the key to make it easier to find  
      the entry in the HashMap after it's been garbage collected.  */   
      private   static   class  SoftValue  extends  SoftReference  {  
        private   final  Object key;  //  always make data member final   
        /**  Did you know that an outer class can access private data  
        members and methods of an inner class?  I didn't know that!  
        I thought it was only the inner class who could access the  
        outer class's private information.  An outer class can also  
        access private members of an inner class inside its inner  
        class.  */   
        private  SoftValue(Object k, Object key, ReferenceQueue q)  {  
          super (k, q);  
          this .key  =  key;  
       }   
     }   

      /**  Here we go through the ReferenceQueue and remove garbage  
      collected SoftValue objects from the HashMap by looking them  
      up using the SoftValue.key data member.  */   
      public   void  processQueue()  {  
       SoftValue sv;  
        while  ((sv  =  (SoftValue)queue.poll())  !=   null )  {  
            if (sv.get() ==   null ) { 
               Log.e( " processQueue " ,  " null " ); 
           } else { 
               Log.e( " processQueue " ,  " Not null " ); 
           }  
         hash.remove(sv.key);  //  we can access private data!  
         Log.e( " SoftHashMap " ,  " release  "   +  sv.key); 
       }   
     }   
      /**  Here we put the key, value pair into the HashMap using  
      a SoftValue object.  */   
      public  Object put(Object key, Object value)  {  
       processQueue();  //  throw out garbage collected values first   
       Log.e( " SoftHashMap " ,  " put into  "   +  key); 
        return  hash.put(key,  new  SoftValue(value, key, queue));  
     }   
      public  Object remove(Object key)  {  
       processQueue();  //  throw out garbage collected values first   
        return  hash.remove(key);  
     }   
      public   void  clear()  {  
       hardCache.clear();  
       processQueue();  //  throw out garbage collected values   
      hash.clear();  
    }   
     public   int  size()  {  
      processQueue();  //  throw out garbage collected values first   
       return  hash.size();  
    }   
     public  Set entrySet()  {  
       //  no, no, you may NOT do that!!! GRRR   
       throw   new  UnsupportedOperationException();  
    }  
  }

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品中文字幕女同| 日韩精品中文字| 一区二区欧美久久| 国产精品成熟老女人| 亚洲天天在线日亚洲洲精| 国产亚洲精品久久久久久| 欧美激情精品久久久久久蜜臀| 国产97在线|日韩| 国产亚洲欧美另类中文| 国产精品国产三级国产aⅴ9色| 国产日韩欧美综合| 成人午夜一级二级三级| 日韩国产高清污视频在线观看| 精品人伦一区二区三区蜜桃网站| 日韩欧美在线视频| 久久久久久免费精品| 国产精品第七影院| 亚洲国语精品自产拍在线观看| 亚洲天堂第二页| 国产精品91免费在线| 2019国产精品自在线拍国产不卡| 欧美在线视频在线播放完整版免费观看| 欧美性猛交xxxx黑人猛交| 国产v综合v亚洲欧美久久| 亚洲va欧美va在线观看| 亚洲影院色在线观看免费| 在线播放国产一区二区三区| 久久国产精品网站| 最新中文字幕亚洲| 久久久精品中文字幕| 欧美日韩人人澡狠狠躁视频| 91精品国产综合久久香蕉922| 久久综合伊人77777| 国产z一区二区三区| 国产亚洲欧美另类中文| 国产精品嫩草影院一区二区| 欧美日韩第一页| 92看片淫黄大片欧美看国产片| 欧美日韩在线第一页| 亚洲国产成人一区| 成人精品视频99在线观看免费| 欧美成人精品在线观看| 国产精品美女免费看| 国产福利精品在线| 亚洲免费一在线| 日韩精品www| 欧美精品日韩www.p站| 91久久精品在线| 国产精品99导航| 国产精品69久久| 中文字幕欧美在线| 最近2019中文字幕一页二页| 亚洲国产精品久久久久秋霞不卡| 日韩黄色高清视频| 97视频在线观看成人| 中文字幕亚洲欧美在线| 成人久久一区二区| 亚洲综合av影视| 久久在线免费观看视频| 欧美成人激情在线| 久久久久久久久国产精品| 欧美高清一级大片| 亚洲国产精品悠悠久久琪琪| 91中文精品字幕在线视频| 91在线观看免费高清| 日韩资源在线观看| 国产欧美日韩免费看aⅴ视频| 国产精品成熟老女人| 亚洲另类激情图| 午夜精品视频网站| 国产精品99久久久久久白浆小说| 欧美性视频精品| 国产噜噜噜噜久久久久久久久| 国产成人精品亚洲精品| 黑人巨大精品欧美一区二区三区| 川上优av一区二区线观看| 成人国产在线激情| 91精品国产91久久久久久| 伊人一区二区三区久久精品| 成人国产在线激情| 日韩少妇与小伙激情| 亚洲欧美制服综合另类| 庆余年2免费日韩剧观看大牛| 91精品综合久久久久久五月天| 欧美激情免费看| 色悠悠国产精品| 精品亚洲va在线va天堂资源站| 国产精品91久久久| 成人免费视频在线观看超级碰| 热草久综合在线| 日韩中文在线不卡| 欧美成人免费全部| 国产999精品视频| 中文字幕自拍vr一区二区三区| 日韩乱码在线视频| 欧洲永久精品大片ww免费漫画| 国产午夜精品视频| 色综合影院在线| 亚洲视频在线观看| 91色视频在线观看| 久久精品久久久久电影| 亚洲视频欧美视频| 欧美日韩亚洲天堂| 国产精品老女人精品视频| 国模精品视频一区二区三区| 亚洲情综合五月天| 成人激情视频在线播放| 欧美国产日韩一区二区| 成人激情综合网| 亚洲偷欧美偷国内偷| 91精品国产乱码久久久久久久久| 欧美激情喷水视频| 国产精品激情av电影在线观看| 亚洲国产欧美一区二区三区久久| 欧美视频专区一二在线观看| 国产一区二区日韩精品欧美精品| 懂色av一区二区三区| 岛国av一区二区三区| 日韩精品一二三四区| 91av在线国产| 欧美网站在线观看| 国产69精品久久久久9| 2019中文字幕全在线观看| 日韩精品有码在线观看| 国产精品电影久久久久电影网| 亚洲视频axxx| 69久久夜色精品国产69乱青草| 久久精品男人天堂| 精品福利在线观看| 1769国产精品| 热久久美女精品天天吊色| 日本在线精品视频| 久久久久久久久久久免费精品| 91免费国产视频| 久久网福利资源网站| 精品国产美女在线| 亚洲国产精品va在线看黑人动漫| 久久久久国产一区二区三区| 亚洲第一精品久久忘忧草社区| 亚洲另类图片色| 亚洲一区国产精品| 国产精品自产拍在线观看| 成人黄色免费网站在线观看| 国产69精品99久久久久久宅男| 亚洲国产精品人人爽夜夜爽| 日韩av免费在线| 欧美性猛交xxxx乱大交蜜桃| 日韩av色综合| 91美女高潮出水| 国产精品扒开腿做爽爽爽男男| 久久久久久久影视| 国产精品扒开腿爽爽爽视频| 欧洲日韩成人av| 成人国产亚洲精品a区天堂华泰| 久久精品中文字幕免费mv| 日韩成人在线视频| 亚洲激情在线观看| 亚洲女同性videos| 国产精品爽爽爽爽爽爽在线观看| 欧美一二三视频| 国产精品久久久久久婷婷天堂| 91精品视频专区| 精品久久中文字幕| 成人精品福利视频|