自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

開發(fā) 后端
一般來說內(nèi)存泄漏有兩種情況。一種情況如在C/C++ 語言中的,在堆中的分配的內(nèi)存,在沒有將其釋放掉的時(shí)候,就將所有能訪問這塊內(nèi)存的方式都刪掉(如指針重新賦值);另一種情況則是在內(nèi)存對象已經(jīng)不需要的時(shí)候,還仍然保留著這塊內(nèi)存和它的訪問方式(引用)。

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

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

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

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

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

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

在這個(gè)例子中,代碼棧中存在Vector 對象的引用 v 和 Object 對象的引用 o 。在 For 循環(huán)中,我們不斷的生成新的對象,然后將其添加到 Vector 對象中,之后將 o 引用置空。問題是當(dāng) o 引用被置空后,如果發(fā)生 GC ,我們創(chuàng)建的 Object 對象是否能夠被 GC 回收呢?答案是否定的。因?yàn)椋?GC 在跟蹤代碼棧中的引用時(shí),會(huì)發(fā)現(xiàn) v 引用,而繼續(xù)往下跟蹤,就會(huì)發(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ù)情況會(huì)出現(xiàn)程序崩潰的情況外,大多數(shù)情況下程序仍然能正常運(yùn)行。但是,在移動(dòng)設(shè)備對于內(nèi)存和 CPU 都有較嚴(yán)格的限制的情況下, Java 的內(nèi)存溢出會(huì)導(dǎo)致程序效率低下、占用大量不需要的內(nèi)存等問題。這將導(dǎo)致整個(gè)機(jī)器性能變差,嚴(yán)重的也會(huì)引起拋出 OutOfMemoryError ,導(dǎo)致程序崩潰。

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

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

例如:

  1. public class FileSearch{  
  2.       private byte [] content;  
  3.       private File 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.      }  

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

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

復(fù)雜數(shù)據(jù)結(jié)構(gòu)中的內(nèi)存泄露問題

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

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


#p#

Java中的幾種引用方式

 

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

 

強(qiáng)引用

 

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

 

軟引用(SoftReference )

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

弱引用(WeakReference )

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

虛引用(PhantomReference )

PhantomReference 類只能用于跟蹤對被引用對象即將進(jìn)行的收集。同樣,它還能用于執(zhí)行  pre-mortem  清除操作。 PhantomReference  必須與  ReferenceQueue  類一起使用。需要  ReferenceQueue  是因?yàn)樗軌虺洚?dāng)通知機(jī)制。當(dāng)垃圾收集器確定了某個(gè)對象是虛可及對象時(shí), PhantomReference  對象就被放在它的  ReferenceQueue  上。將  PhantomReference  對象放在  ReferenceQueue  上也就是一個(gè)通知,表明  PhantomReference  對象引用的對象已經(jīng)結(jié)束,可供收集了。這使您能夠剛好在對象占用的內(nèi)存被回收之前采取行動(dòng)。 Reference與 ReferenceQueue 的配合使用。

GC、 Reference 與 ReferenceQueue 的交互

A、  GC無法刪除存在強(qiáng)引用的對象的內(nèi)存。
B、  GC發(fā)現(xiàn)一個(gè)只有軟引用的對象內(nèi)存,那么:
①  SoftReference對象的 referent  域被設(shè)置為 null ,從而使該對象不再引用 heap 對象。
②  SoftReference引用過的 heap 對象被聲明為 finalizable 。
③  當(dāng) heap  對象的  finalize()  方法被運(yùn)行而且該對象占用的內(nèi)存被釋放, SoftReference  對象就被添加到它的  ReferenceQueue (如果后者存在的話)。
C、  GC發(fā)現(xiàn)一個(gè)只有弱引用的對象內(nèi)存,那么:
①  WeakReference對象的 referent 域被設(shè)置為 null , 從而使該對象不再引用heap 對象。
②  WeakReference引用過的 heap 對象被聲明為 finalizable 。
③  當(dāng)heap 對象的 finalize() 方法被運(yùn)行而且該對象占用的內(nèi)存被釋放時(shí), WeakReference 對象就被添加到它的 ReferenceQueue (如果后者存在的話)。
D、  GC發(fā)現(xiàn)一個(gè)只有虛引用的對象內(nèi)存,那么:
①  PhantomReference引用過的 heap 對象被聲明為 finalizable 。
②  PhantomReference在堆對象被釋放之前就被添加到它的 ReferenceQueue 。
 
值得注意的地方有以下幾點(diǎn):
1、 GC 在一般情況下不會(huì)發(fā)現(xiàn)軟引用的內(nèi)存對象,只有在內(nèi)存明顯不足的時(shí)候才會(huì)發(fā)現(xiàn)并釋放軟引用對象的內(nèi)存。
2、 GC 對弱引用的發(fā)現(xiàn)和釋放也不是立即的,有時(shí)需要重復(fù)幾次 GC ,才會(huì)發(fā)現(xiàn)并釋放弱引用的內(nèi)存對象。
3、軟引用和弱引用在添加到 ReferenceQueue 的時(shí)候,其指向真實(shí)內(nèi)存的引用已經(jīng)被置為空了,相關(guān)的內(nèi)存也已經(jīng)被釋放掉了。而虛引用在添加到 ReferenceQueue 的時(shí)候,內(nèi)存還沒有釋放,仍然可以對其進(jìn)行訪問。
代碼示例
通過以上的介紹,相信您對Java 的引用機(jī)制以及幾種引用方式的異同已經(jīng)有了一定了解。光是概念,可能過于抽象,下面我們通過一個(gè)例子來演示如何在代碼中使用 Reference 機(jī)制。
  1. String str  =   new  String( " hello " );  // ①   
  2. ReferenceQueue < String >  rq  =   new  ReferenceQueue < String > ();  // ②   
  3. WeakReference < String >  wf  =   new  WeakReference < String > (str, rq);  // ③   
  4. str = null ;  // ④取消"hello"對象的強(qiáng)引用   
  5. String str1 = wf.get();  // ⑤假如"hello"對象沒有被回收,str1引用"hello"對象  
  6. // 假如"hello"對象沒有被回收,rq.poll()返回null   
  7. Reference <?   extends  String >  ref = rq.poll();  // ⑥  

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

引用機(jī)制與復(fù)雜數(shù)據(jù)結(jié)構(gòu)的聯(lián)合應(yīng)用

了解了GC 機(jī)制、引用機(jī)制,并配合上 ReferenceQueue ,我們就可以實(shí)現(xiàn)一些防止內(nèi)存溢出的復(fù)雜數(shù)據(jù)類型。

例如,SoftReference 具有構(gòu)建 Cache 系統(tǒng)的特質(zhì),因此我們可以結(jié)合哈希表實(shí)現(xiàn)一個(gè)簡單的緩存系統(tǒng)。這樣既能保證能夠盡可能多的緩存信息,又可以保證 Java 虛擬機(jī)不會(huì)因?yàn)閮?nèi)存泄露而拋出 OutOfMemoryError 。這種緩存機(jī)制特別適合于內(nèi)存對象生命周期長,且生成內(nèi)存對象的耗時(shí)比較長的情況,例如緩存列表封面圖片等。對于一些生命周期較長,但是生成內(nèi)存對象開銷不大的情況,使用WeakReference 能夠達(dá)到更好的內(nèi)存管理的效果。

#p#

SoftHashmap 的源碼一份,相信看過之后,大家會(huì)對 Reference 機(jī)制的應(yīng)用有更深入的理解。

  1. package  com. *** .widget;  
  2.     // : SoftHashMap.java    
  3.     import  java.util. * ;   
  4.     import  java.lang.ref. * ;   
  5.     import  android.util.Log;  
  6.      
  7.     public   class  SoftHashMap  extends  AbstractMap  {   
  8.       /**  The internal HashMap that will hold the SoftReference.  */    
  9.       private   final  Map hash  =   new  HashMap();   
  10.       /**  The number of "hard" references to hold internally.  */    
  11.       private   final   int  HARD_SIZE;   
  12.       /**  The FIFO list of hard references, order of last access.  */    
  13.       private   final  LinkedList hardCache  =   new  LinkedList();   
  14.       /**  Reference queue for cleared SoftReference objects.  */    
  15.       private  ReferenceQueue queue  =   new  ReferenceQueue();   
  16.       // Strong Reference number   
  17.       public  SoftHashMap()  {  this ( 100 ); }    
  18.       public  SoftHashMap( int  hardSize)  { HARD_SIZE  =  hardSize; }    
  19.        
  20.       public  Object get(Object key)  {   
  21.        Object result  =   null ;   
  22.         //  We get the SoftReference represented by that key    
  23.        SoftReference soft_ref  =  (SoftReference)hash.get(key);   
  24.         if  (soft_ref  !=   null )  {   
  25.           //  From the SoftReference we get the value, which can be   
  26.           //  null if it was not in the map, or it was removed in   
  27.           //  the processQueue() method defined below    
  28.         result  =  soft_ref.get();   
  29.           if  (result  ==   null )  {   
  30.             //  If the value has been garbage collected, remove the   
  31.             //  entry from the HashMap.    
  32.            hash.remove(key);   
  33.          }   else   {   
  34.             //  We now add this object to the beginning of the hard   
  35.             //  reference queue.  One reference can occur more than   
  36.             //  once, because lookups of the FIFO queue are slow, so   
  37.             //  we don't want to search through it each time to remove   
  38.             //  duplicates.   
  39.               // keep recent use object in memory   
  40.            hardCache.addFirst(result);   
  41.             if  (hardCache.size()  >  HARD_SIZE)  {   
  42.               //  Remove the last entry if list longer than HARD_SIZE    
  43.              hardCache.removeLast();   
  44.            }    
  45.          }    
  46.        }    
  47.         return  result;   
  48.      }    
  49.     
  50.       /**  We define our own subclass of SoftReference which contains   
  51.       not only the value but also the key to make it easier to find   
  52.       the entry in the HashMap after it's been garbage collected.  */    
  53.       private   static   class  SoftValue  extends  SoftReference  {   
  54.         private   final  Object key;  //  always make data member final    
  55.         /**  Did you know that an outer class can access private data   
  56.         members and methods of an inner class?  I didn't know that!   
  57.         I thought it was only the inner class who could access the   
  58.         outer class's private information.  An outer class can also   
  59.         access private members of an inner class inside its inner   
  60.         class.  */    
  61.         private  SoftValue(Object k, Object key, ReferenceQueue q)  {   
  62.           super (k, q);   
  63.           this .key  =  key;   
  64.        }    
  65.      }    
  66.     
  67.       /**  Here we go through the ReferenceQueue and remove garbage   
  68.       collected SoftValue objects from the HashMap by looking them   
  69.       up using the SoftValue.key data member.  */    
  70.       public   void  processQueue()  {   
  71.        SoftValue sv;   
  72.         while  ((sv  =  (SoftValue)queue.poll())  !=   null )  {   
  73.             if (sv.get() ==   null ) {  
  74.                Log.e( " processQueue " ,  " null " );  
  75.            } else {  
  76.                Log.e( " processQueue " ,  " Not null " );  
  77.            }   
  78.          hash.remove(sv.key);  //  we can access private data!   
  79.          Log.e( " SoftHashMap " ,  " release  "   +  sv.key);  
  80.        }    
  81.      }    
  82.       /**  Here we put the key, value pair into the HashMap using   
  83.       a SoftValue object.  */    
  84.       public  Object put(Object key, Object value)  {   
  85.        processQueue();  //  throw out garbage collected values first    
  86.        Log.e( " SoftHashMap " ,  " put into  "   +  key);  
  87.         return  hash.put(key,  new  SoftValue(value, key, queue));   
  88.      }    
  89.       public  Object remove(Object key)  {   
  90.        processQueue();  //  throw out garbage collected values first    
  91.         return  hash.remove(key);   
  92.      }    
  93.       public   void  clear()  {   
  94.        hardCache.clear();   
  95.        processQueue();  //  throw out garbage collected values    
  96.       hash.clear();   
  97.     }    
  98.      public   int  size()  {   
  99.       processQueue();  //  throw out garbage collected values first    
  100.        return  hash.size();   
  101.     }    
  102.      public  Set entrySet()  {   
  103.        //  no, no, you may NOT do that!!! GRRR    
  104.        throw   new  UnsupportedOperationException();   
  105.     }   
  106.   }  

原文鏈接:http://henryyang.iteye.com/blog/1188328

編輯推薦:

  1. 什么是JavaScript異步編程
  2. 使用HTML 5和Javascript設(shè)計(jì)繪圖程序
  3. 多核平臺(tái)下的Java優(yōu)化
  4. Java開發(fā)框架Play框架快速入門
  5. 利用Java實(shí)現(xiàn)電子郵件的批量發(fā)送

 

責(zé)任編輯:林師授 來源: henryyang的博客
相關(guān)推薦

2015-05-14 15:38:40

javajava內(nèi)存泄露

2009-06-16 11:11:07

Java內(nèi)存管理Java內(nèi)存泄漏

2010-09-25 11:23:15

Java內(nèi)存泄露

2017-05-04 16:07:11

Tomcat內(nèi)存泄露

2010-10-25 10:10:27

ibmdwJava

2015-12-07 09:39:27

Java內(nèi)存泄露

2010-08-12 09:30:08

Flex內(nèi)存泄露

2011-06-16 10:27:55

.NET內(nèi)存泄漏

2017-02-21 16:40:16

Android垃圾回收內(nèi)存泄露

2010-07-29 14:08:05

Flex內(nèi)存泄露

2018-07-17 08:58:57

垃圾回收內(nèi)存泄露方法

2013-12-23 09:25:21

2010-09-25 11:32:24

Java內(nèi)存泄漏

2015-07-16 15:16:41

內(nèi)存泄露解決辦法

2013-08-07 10:07:07

Handler內(nèi)存泄露

2013-12-17 16:01:02

iOSXcodeInstruments

2013-07-11 10:37:20

Java內(nèi)存模型

2010-06-02 13:00:43

Linux 內(nèi)存監(jiān)控

2010-08-10 10:10:34

Flex內(nèi)存泄露

2020-05-27 21:13:27

JavaJVM內(nèi)存
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號