CodeGym /Java Blog /Toto sisi /對像生命週期
John Squirrels
等級 41
San Francisco

對像生命週期

在 Toto sisi 群組發布
你好!如果我告訴您您的計算機內存量有限,我想您不會感到驚訝 :)
對像生命週期 - 1
即使是您的硬盤(其大小是 RAM 的很多很多倍)也可能會塞滿您最喜歡的遊戲、電視節目和其他內容。為防止這種情況發生,您需要監控計算機內存的當前狀態並刪除不需要的文件。所有這些與 Java 編程有什麼關係?很直接!畢竟,創建任何對像都會導致 Java 機器為其分配內存。一個大型的現實世界程序會創建數万或數十萬個對象,並為每個對象分配一塊內存。但是您怎麼看,這些對像中有多少存在?在我們的程序運行時,它們是否一直“活著”?當然不是。即使具有所有優點,Java 對像也不是不朽的 :)對像有自己的生命週期。 今天我們將暫停編寫代碼並探索這個過程 :) 這對於理解程序如何工作和管理資源也非常重要。那麼,一個物體的生命從哪裡開始呢?就像人一樣,從出生開始,即當它被創造時。

Cat cat = new Cat();// Our Cat object's lifecycle begins now!
首先,Java 虛擬機分配創建對象所需的內存。然後它創建一個對它的引用(在我們的例子中是cat),以便跟踪它。然後初始化所有變量,調用構造函數,我們的新對象現在過著自己的生活:)對象的生命週期各不相同。這裡沒有確切的數字。在任何情況下,對像都存在於程序中並在一段時間內執行其功能。準確地說,只要有對它的引用,這個對象就是“活著的”。一旦沒有引用,對象就會“死亡”。例如:

public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}
在該main()方法中,“Lamborghini Diablo”Car 對像在第二行不再存在。只有一個對它的引用,並且該引用被設置為 null。由於沒有剩餘的暗黑破壞神參考,它變成了“垃圾”。不必將引用設置為零即可發生這種情況:

public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}
這裡我們創建了第二個對象並將其分配給 lamborghini 引用。現在有兩個引用指向該Lamborghini Gallardo對象,但該Lamborghini Diablo對像沒有。這意味著Diablo對像變成了垃圾。這是 Java 的內置垃圾收集器(GC) 發揮作用的時候。
對像生命週期 - 2
垃圾收集器是一種內部 Java 機制,負責釋放內存,即從內存中刪除不需要的對象。我們選擇用掃地機器人來代表它是有原因的。垃圾收集器的工作方式大致相同:它在後台“移動”您的程序,收集垃圾。您幾乎不必與它互動。它的工作是刪除程序中不再使用的對象。因此,它為其他對象釋放了內存。您還記得我們在課程開始時說過在現實生活中您必須監控計算機的狀態並刪除舊文件嗎?如果我們談論的是 Java 對象,垃圾收集器會為您做這件事. 垃圾收集器在您的程序運行時多次啟動:您不必顯式調用它並給它命令(儘管這在技術上是可行的)。稍後我們將更多地討論垃圾收集器,並更詳細地分析它是如何工作的。finalize()當垃圾收集器到達一個對象時——就在它被銷毀之前——調用該對象的特殊方法。可以調用此方法來釋放對象使用的某些額外資源。該finalize()方法屬於 Object 類。換句話說,它類似於equals(), hashCode()and toString()(您之前遇到過)。每個對像都有它。和其他方法不同的是……這怎麼說呢……很任性。我們的意思是它並不總是在對像被銷毀之前調用。編程是一項非常精確的活動。程序員告訴計算機做某事,計算機就會去做。我假設您已經習慣了這種行為,所以一開始您可能很難接受以下想法:“在對像被銷毀之前,調用 Object 類的方法。或者不調用。finalize()如果我們走運的話! “ 不過,這就是現實。Java 機器根據具體情況自行決定是否調用 finalize()。作為實驗,讓我們嘗試運行以下代碼:

public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {

       for (int i = 0 ; i < 1000000; i++) {

           Cat cat = new Cat();
           cat = null;// The first object becomes available for garbage collection here
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("The Cat is destroyed!");
   }
}
我們創建一個Cat對象,並在下一行中將對它的唯一引用清零。我們這樣做了一百萬次。我們已經明確地覆蓋了該finalize()方法。每次對Cat像被銷毀時,它都必須顯示一個字符串——總共一百萬次。但不是!確切地說,在我的電腦上只執行了37346次!換句話說,我的 Java 機器決定finalize()在每 27 個案例中只有 1 個調用該方法。在其他情況下,垃圾回收不涉及此調用。嘗試自己運行這段代碼。您很可能會得到不同的結果。如您所見,很難找到finalize()可靠的合作夥伴 :) 因此,這裡有一個關於未來的小提示:不要依賴finalize()釋放關鍵資源的方法。JVM 可能會調用它,也可能不會。誰知道?如果您的對像在存在時持有一些對性能至關重要的資源(例如,一個打開的數據庫連接),那麼最好創建並顯式調用一個特殊的方法來在不再需要該對象時釋放它們。這樣,您就可以確定您的程序的性能不會受到影響。 我們一開始就說使用內存和垃圾收集是非常重要的主題,確實如此。資源處理不當和誤解如何清理不必要的對象會導致最令人不快的錯誤之一:內存洩漏。這是最著名的編程錯誤之一。它甚至有自己的維基百科文章. 編寫不當的代碼可能會導致每次都為新創建的對象分配內存,但舊的、不必要的對象無法用於垃圾回收。既然我們已經用機器人吸塵器做了類比,想像一下如果在運行機器人之前你把襪子撒得到處都是,打碎了一個玻璃花瓶,把樂高積木積木弄得滿地都是,會發生什麼。自然地,機器人會嘗試做某事,但總有一天它會卡住。
對像生命週期 - 3
為了讓真空吸塵器正常運行,您需要保持地板的良好形狀並撿起它無法處理的所有東西。垃圾收集器遵循相同的原則。如果一個程序有很多它無法清理的對象(比如我們的機器人吸塵器的襪子或樂高積木),總有一天我們會耗盡內存。不僅您的程序會掛起,所有其他恰好在計算機上運行的程序也會掛起。畢竟,它們也不會有足夠的內存(回到我們的類比,地板上的碎玻璃不僅會停止真空吸塵器,還會停止住家裡的人)。簡而言之,這就是 Java 中對像生命週期和垃圾回收的樣子。您無需記住這一點:只需了解其工作原理就足夠了。下一節課,我們 我們將更詳細地返回這些過程。但現在,您可以返回解決 CodeGym 任務 :) 祝你好運!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION