Java教室: Garbage Collection 清除物件的順序 (轉)

worldblog發表於2007-12-04
Java教室: Garbage Collection 清除物件的順序 (轉)[@more@]

 ----------------------------------------------------------------
 
  (作者 : 艾群科技  蕭松瀛) 
 
  與 C 的其中一個差別就在於寫 C 程式時,如果我們 new 了一個
 物件,同時我們也必須下  delete 來清除它, C 語言的核心並不會判斷
 這個物件是否不再使用,而在程式時,需不需要將物件移除,也全都
 由使用者自行控制。其實這個原意很好,但是寫到大型專案,或是 Multi
 -Thread 程式時,往往這些物件會變成難以控制,有時候我們甚至會試圖
 去使用一個已經不存在的指標。 
 
  在 Java 中, JVM 幫我們管理這一層,因此當物件不在被使用時, JV
  M 會在"適當"的時間點將它由記憶體中移除。 
 
  所有的物件均繼承 ,因此 Object 這個物件中,有一個很有用
 的 method 叫做 finalize() ,這一個 method 依照定義,當 Garbage C
 ollection 要將這個物件移除前會先呼叫這個物件的 finalize() 。也就
 是說,當我們在實作我們的 Class 的時候,最好將這一個 method overr
  。這樣一來,當 Garbage Collection 要清除物件的時候,就會執行
 到我們自己寫的  finalize()。 
 
  因此我們設計了底下這個物件,這個物件在我們建立時,需要帶入一個
  int 值,這一個 int 值,而當 finalize() 被執行的時候,他會將這個
  int 值列印出來,好讓我們知道 Garbage Collection正準備清除哪一個
 物件。 
 
  public class testobject 
  { 
  private int itell; 
  public testobject() 
  { 
  this.itell = 0; 
  } 
  public testobject(int i) 
  { 
  this.itell = i; 
  } 
  public void finalize() 
  { 
  System.out.println("testobject id = "+itell); 
  } 
  } 
 
  接著,我寫了底下這一個 testfinalize.java ,這一個 testfinalize
 .java 會宣告一個陣列,這一個陣列中,會存放 testobject 物件,屆時
  我會將這一個陣列設為 null ,表示我不再使用這一個陣列了。 
 
  public class testfinalize 
  { 
  public static void main(String argv[]) 
  { 
  testobject [] testo = new testobject[2]; 
  testo[0] = new testobject(1); 
  testo[1] = new testobject(2); 
  testo = null; 
  System.out.println("finish..."); 
  } 
  } 
 
  執行的結果是 
 
  C:tempjavajava testfinalize 
  finish... 
 
  C:tempjava 
 
  奇怪,是 garbage collection 沒有執行 finalize() 嗎?? 不是的,
 是 garbage collection 根本沒有被啟動,在前面說過,garbage collec
 tion 會在適當的時機啟動,而這一個 testfinalize 程式很小, JVM 認
 為不需要執行  Garbage Collection ,因此程式結束,記憶體釋放。 
 
  是不是這樣就沒辦法知道 garbage collection 做了時麼事情??不是我
 們還可以透過兩個 method 來建議 JVM 執行 garbage collection,一個
 是 System.gc(); ,另外一個建議的方式是 Runtime.getRuntime().gc()
 , System.gc() 也會執行 Runtime.getRuntime().gc() ,不論哪一個,
 他們都會建議  JVM 去執行另外一個 Thread ,這一個 Thread 會掃瞄所
 有沒有被用到的物件,將他們清除,並且取得他們所佔用的記憶體。因此
 我們將  testfinalize.java 加上一行如下 
 
  public class testfinalize 
  { 
  public static void main(String argv[]) 
  { 
  testobject [] testo = new testobject[2]; 
  testo[0] = new testobject(1); 
  testo[1] = new testobject(2); 
  testo = null; 
  System.gc(); 
  System.out.println("finish..."); 
  } 
  } 
 
  Compile  之後執行結果如下 
  C:tempjavajava testfinalize 
  testobject id = 1 
  testobject id = 2 
  finish... 
 
  C:tempjava 
 
  沒錯,我們看到了 testobject id = 1 以及 testobject id = 2 這兩
 行了。 
 
  問題就這樣解決了嗎??非也!他事先清除陣列 testo[0] 接著再清除 t
  esto[1] 還是有其他的順序?? 
 
  首先,我們先來看看 JLS 中對於 Garbage Collection 順序的說明 
 
  12.6.2 Finalizer Invocations are Not Ordered 
 
  The Java programming language imposes no ordering on finalize
 method calls. Finalizers may be called in any order, or even con
 currently. As an example, if a circularly linked group of unfina
 lized objects becomes unreachable (or finalizer-reachable), then
  all the objects may become finalizable together. Eventually, th
 e finalizers for these objects may be invoked, in any order, or
 even concurrently using multiple threads. If the automatic stora
 ge manager later finds that the objects are unreachable, then th
  eir storage can be reclaimed. 
 
  It is straightforward to implement a class that will cause a s
 et of finalizer-like methods to be invoked in a specified order
 for a set of objects when all the objects become unreachable. De
  fining such a class is left as an exercise for the reader. 
 
  依照這個規格說明,我們發現, JVM 並沒有依照一定的順序來清除物
 件,然而實際上呢 ?? 
 
  我們再修改 testfinalize.java 讓他成為底下的 
 
  public class testfinalize 
  { 
  public static void main(String argv[]) 
  { 
  testobject [] testo = new testobject[2]; 
  testo[1] = new testobject(2); 
  testo[0] = new testobject(1); 
  testo = null; 
  System.gc(); 
  System.out.println("finish..."); 
  } 
  } 
 
  也就是這一次,我們先產生 new testobject(2) 並且將他放到 testo[
  1] 然後再產生 new testobject(1),接著放到 testo[0] 。 
 
  執行結果如下 
  C:tempjavajava testfinalize 
  testobject id = 2 
  testobject id = 1 
  finish... 
 
  C:tempjava 
 
  沒錯,我們實際上執行這個程式的結果是先產生的物件, Garbage Col
  lection 先將他移除。 
 
  至於時麼時候該使用 finalize() ? 通常僅需要記住一個原則,就是如
 果在產生物件的同時,我們有使用到一些資源,譬如 JC 連線,譬如開
 了個 socket 到另外一臺機器,甚至開了個 Server等等,那麼在
  finalize() 的時候,我們最好就需要嘗試著將這些資源給釋放。 
 
 
  執行環境 
  1. Sun Ultra 10 
  1.3.0 Hotspot Client 
  2. 2000 
  JDK 1.3.0_01 Hotspot Client
 
 ----------------------------------------------------------------

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988243/,如需轉載,請註明出處,否則將追究法律責任。

相關文章