Java對象是否回收的標準是:是否有引用變量引用該對象。java
當一個對象被建立以後,GC會實時的監控每個對象的狀態,包括對象的申請,引用,被引用,賦值等。當其再也不被引用時,就會被回收。數據庫
若是把Java的程序看作是一個有向圖,若是從運行的起點可以到達某個對象,那麼它就是可達狀態,不然就處於不可達狀態數組
eg:緩存
參見圖:性能
能夠看到「第三個節點」並無被任何其餘對象引用,它處於不可達狀態,GC會適時的回收它。ui
三種狀態:spa
爲了更好的管理對象的引用,Java從1.2開始提供了三個類SoftReference,PhantomReference,WeakReference,概括起來:對象
強引用生命週期
軟引用內存
弱引用
虛引用
JVM是不會回收強引用的對象,即便內存不足的時候,因此它是形成Java泄露的主要緣由。
內存泄露是指系統分配了內存,可是有些無用的內存卻沒有被收回,致使內存耗損愈來愈大。
雖然JVM的垃圾回收機制能夠回收沒有被引用的內存,可是要考慮,這個「垃圾」的含義,對於程序來講是垃圾,由於再也不使用它們了,可是若是它們還被引用着,卻對JVM來講不是垃圾。
在ArrayList的remove方法源碼中:
elementData[--size] = null;
就是爲了讓垃圾回收器回收的,不然就會產生內存泄露—刪除了一個對象,可是該對象所佔據的空間並不會被釋放。
好比:
左圖能夠看出,ArrayList的長度爲4,當刪除最後一個元素的時候(即指向d的元素),若是沒有elementData[--size] = null,那麼它就形如右圖,第四個元素已經沒法被該ArrayList訪問了(由於刪除以後ArrayList的長度爲3了),可是它仍然指向「d」,即這塊內存沒有被釋放,gc也不會回收它,那麼隨着使用次數多了,內存損耗越來愈大,就形成了內存泄露。
1.儘可能只用直接量建立String(或者基礎類的包裝類),好比:
String str = 「hello」
而不要使用String str = new String(「hello」);
雖然二者都會被JVM的字符串緩衝池緩衝,可是後者str所引用的String對象在底層還包含了一個char[]數組,該數組依次存放了h,e,l,l,o
另外,在字符串緩衝池中的字符串不會被gc回收
2.使用StringBuffer和StringBuilder,這二者是可變的。當進行字符串運算時,直接使用String,將會產生大量的臨時字符串,下降性能。
3.儘早釋放無用對象的引用
好比:
若是在obj = null;以後程序就執行完畢的話,這句話就沒有必要書寫了,由於局部變量會隨着方法執行完畢而自行銷燬,可是若是它後面還須要執行其餘的耗時操做,那麼仍是及時的將其釋放。
4.儘可能少使用靜態變量
由於static變量的生命週期是和類同步的,因此在類不被卸載的狀況下,靜態變量也會一直存儲在內存中
5.不要在常常調用的方法,循環中建立java對象
這兩種狀況的特徵是這些代碼會被常常調用,若是常常建立對象,雖然對象的生命週期不長,可是系統會不斷的分配,回收內存,致使性能降低。
6.緩存常常使用的對象
好比數據庫鏈接池,一般有兩種方式:
1. 使用HashMap緩衝
2. 使用開源項目
7.儘可能不要使用finalize方法
8.考慮使用軟引用