程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 懂得Java中的內存洩漏及處理辦法示例

懂得Java中的內存洩漏及處理辦法示例

編輯:關於JAVA

懂得Java中的內存洩漏及處理辦法示例。本站提示廣大學習愛好者:(懂得Java中的內存洩漏及處理辦法示例)文章只能為提供參考,不一定能成為您想要的結果。以下是懂得Java中的內存洩漏及處理辦法示例正文


本文具體地引見了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();  
    }  
  }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved