對於基本類型變量來說,因為都被分配在內存棧中,因此釋放不是問題,而且都會被及時地釋放.但對於引用類型來說,就不一樣了.
Java提供了基本的對象內存回收機制-垃圾回收器.下面看看如何工作:
首先我們必須了解一點,引用類型的變量和基本類型的變量一樣,都是被分配到內存棧裡的,只是引用類型的變量在棧中保存的是一個引用地址(指針),該引用地址所指的堆內存就是實際的對象存放區。
Java的垃圾回收器對於Java程序來說是外部程序,是獨立運行的,因此必須有一種機制,讓垃圾回收器知道哪些對象(堆中)是不再被用的,最簡單的方法當然是對對象引用計數:
一個對象被一個對象變量引用,其對象引用計數就加1,如果一個對象的引用變量(在棧中)超出生存期,被釋放時,對象引用計數就減1,這樣,垃圾回收器就可以根據對象引用計數來判斷對象是否可以被收回。看起來這種方法確實比較簡單,但實際上,這種方式還是有很多問題的:
1)如果對象變量都在棧中,上述機制當然沒問題。但實際上對象變量可以是另一個對象的成員,而且對象本身也可以包含其它對象類型的成員變量,這樣就會形成一個復雜的引用關系網,這個引用關系網還可能會包含循環引用的情況,就會很容易導致一些對象在整個程序運行期間都不會被回收,從而產生大量的內存洩露;
2)同步問題。垃圾回收器工作時,如果程序也在工作,也在分配或操作對象,比如一個對象的引用計數雖然為0,垃圾回收器回收該對象,但同時,這個對象又被引用到一個新的對象變量,就會產生不可預知的錯誤。為了防止這種沖突,垃圾回收器工作時程序就必須停止工作。這就會帶來另外一個問題,程序運行效率降低。
現在這種利用對象引用計數方式的垃圾回收方式已經很少單獨使用。
另外一種方法是采用對象跟蹤技術的方法:
垃圾回收器工作時,從程序棧內存中的引用變量開始,遍歷對象引用,標記對象可達。那麼不可達的對象都可以回收掉。
這種方法解決了循環引用的問題,回收效率比較高,但也同樣有以下問題:
1)沒有解決垃圾回收器和程序之間的同步問題,垃圾回收器工作期間程序必須停止運行;
2) 每次垃圾回收時,都需要遍歷所有的對象,用時會比較長;
當然還有很多垃圾回收策略,大家可以搜一下,雖然垃圾回收對於程序員來說,大部分時間是透明的,但理解這些機制,其實也可以對我們處理實際問題時提供一種借鑒。
無論是那種回收機制,對於程序員來說,有兩個地方是必須關注的:
1)我們的對象什麼時候被回收(垃圾回收器運行的不可控);
2)Java產生的對象沒問題,但我們自己分配的內存(主要是調用其它語言代碼庫,比如C++)怎麼釋放。
垃圾回收器工作的觸發是根據其對內存使用的監控來確定的,當內存空間小於某個閥值時才觸發垃圾回收。對於大部分的情況,這應該不是問題,但如果我們的對象使用了第三方稀缺資源,比如數據庫鏈接,或者我們需要在對象釋放的時候做一些清除(比如擦除在界面上的繪圖)工作,我們該怎麼辦?一種辦法當然是主動出擊,比如數據庫鏈接,打印機釋放等,但有些情況下,我們還是無法主動出擊的,比如一個對象使用了數據庫鏈接,但這個對象被多個其它對象共享,你就很難主動出擊釋放鏈接了。對於這種情況,Java提供了另外一種機制,就是為每個對象提供一個finalize()方法,該方法由垃圾回收器調用(雖然你自己也可以調用,但不建議這樣做,原因同無法主動出擊類似)。
這樣對於對象釋放後需要做的清理工作就可以在這個方法中進行。但要注意垃圾回收器對finalize()方法的調用是有限制的,比如每個對象只調用一次這個方法,因此,我們必須留意對象"復活"所帶來的問題。
對於什麼時候我們的對象會被回收的問題,雖然我們可以調用System.gc()來建議(注意,只是建議)垃圾回收器進行回收工作,但實際上對我們來說,還是不可控的,因此:
1)對於資源,特別是稀缺資源的釋放,我們還是應該盡量主動出擊來完成;
2)對於調用其它語言代碼庫,也盡量主動出擊進行對象釋放,如果不行,至少也要在finalize()方法中完成;
下面的例子,是抄書本上的,用來演示垃圾回收的不確定性(我稍微做了修改):
public class Chair {
static boolean gcrun = false;
static boolean f = false;
static int created = 0;
static int finalized = 0;
int i;
Chair() {
i = ++created;
if(created == 47)
System.out.println("Created 47");
}
protected void finalize() {
if(!gcrun) {
gcrun = true;
System.out.println(
"Beginning to finalize after " +
created + " Chairs have been created=>"+i);
}
if(i == 47) {
System.out.println(
"Finalizing Chair #47, " +
"Setting flag to stop Chair creation");
f = true;
}
finalized++;
if(finalized >= created)
System.out.println("All " + finalized + " finalized");
}
}
public class GarbageC {
public static void main(String[] args) {
while(!Chair.f) {
new Chair();
new String("To take up space");
}
System.out.println(
"After all Chairs have been created:\n" +
"total created = " + Chair.created +
", total finalized = " + Chair.finalized);
String theMethString ="after";
if(theMethString.equals("before")) {
System.out.println("gc():");
System.gc();
System.out.println("runFinalization():");
System.runFinalization();
}
System.out.println("bye!");
if(theMethString.equals("after"))
System.runFinalizersOnExit(true);
}
}