java物件的初始化和回收
物件的初始化和回收
系統生成物件時,會自動為物件分配記憶體空間,並呼叫相應的建構函式對其初始化,在物件沒有任何引用繫結時,垃圾回收器會週期行掃描記憶體情況並進行回收。
- 初始化
物件的生成和初始化是一體的概念,生成了物件就會對其初始化。Java提供了四種方法生成物件,使用new關鍵字僅僅是其中一種方法。
1. 使用new關鍵字生成物件。
2. 使用反射機制動態生成物件。利用Class,ClassLoader和Constructor類的方法動態生成物件。詳情會在Java反射機制中解釋。
3. 使用克隆生成物件。如果一個類實現了Cloneable介面,這個類的物件可以通過使用clone()方法生成該物件的一份拷貝。
4. 使用反序列化從流中生成對物件。從磁碟中讀取相應的輸入流的讀取方法來生成新的物件。這個方法將在序列化中詳細解釋。
- 回收
垃圾回收是系統自動完成的。當一個物件不再使用,系統會將引用變數的值賦為null,同時呼叫物件的finalize()方法銷燬物件。
finalize()是一個被保護的方法,可由子類對其複寫,需要注意的是,java機制並不保證該方法一定會被執行,如果向顯式呼叫,可以使用System.gc(),詳細資訊會在JVM中描述。
1. 垃圾回收
垃圾回收是Java程式設計中記憶體管理的核心概念,JVM的記憶體管理機制被稱為垃圾回收機制。
一個物件建立後被放置在JVM的堆記憶體中,當永遠不再引用這個物件時,它將被JVM在堆記憶體中回收。被建立的物件不能再生,同時也沒有辦法通過程式語句釋放它們。即當物件在JVM執行空間中無法通過根集合到達(找到)時,這個物件被稱為垃圾物件。根集合是由類中的靜態引用域與本地引用域組成的。JVM通過根集合索引物件。
在做Java應用開發時經常會用到由JVM管理的兩種型別的記憶體:堆記憶體和棧記憶體。簡單來講,堆記憶體主要用來儲存程式在執行時建立或例項化的物件與變數。例如通過new關鍵字建立的物件。而棧記憶體則是用來儲存程式程式碼中宣告為靜態或非靜態的方法。
(1) 堆記憶體
堆記憶體在JVM啟動的時候被建立,堆記憶體中所儲存的物件可以被JVM自動回收,不能通過其他外部手段回收,也就是說開發人員無法通過新增相關程式碼的手段來回收堆記憶體中的物件。堆記憶體通常情況下被分為兩個區域:新物件區域與老物件區域。
新物件區域:又可細分為三個小區域:伊甸園區域、From區域與To區域。伊甸園區域用來儲存新建立的物件,它就像一個堆疊,新的物件被建立,就像指向該棧的指標在增長一樣,當伊甸園區域中的物件滿了之後,JVM系統將要做到可達性測試,主要任務是檢測有哪些物件由根集合出發是不可達的,這些物件就可以被JVM回收,並且將所有的活動物件從伊甸園區域拷貝到To區域,此時一些物件將發生狀態交換,有的物件就從To區域被轉移到From區域,此時From區域就有了物件。上面物件遷移的整個過程,都是由JVM控制完成的。
老物件區域:在老物件區域中的物件仍然會有一個較長的生命週期,大多數的JVM系統垃圾物件,都是源於"短命"物件,經過一段時間後,被轉入老物件區域的物件,就變成了垃圾物件。此時,它們都被打上相應的標記,JVM系統將會自動回收這些垃圾物件,建議不要頻繁地強制系統作垃圾回收,這是因為JVM會利用有限的系統資源,優先完成垃圾回收工作,導致應用無法快速地響應來自使用者端的請求,這樣會影響系統的整體效能。
(2) 棧記憶體
堆記憶體主要用來儲存程式在執行時建立或例項化的物件與變數。例如通過new關鍵字建立的物件。而棧記憶體則是用來儲存程式程式碼中宣告為靜態或非靜態的方法。
2. JVM中物件的生命週期
在JVM執行空間中,物件的整個生命週期大致可以分為7個階段:
建立階段;
應用階段;
不可視階段;
不可到達階段;
可收集階段;
終結階段;
釋放階段
上面這7個階段,構成了JVM中物件的完整的生命週期。
(1) 建立階段
在物件的建立階段,系統主要通過下面的步驟,完成物件的建立過程:
<1> 為物件分配儲存空間;
<2> 開始構造物件;
<3> 從超類到子類對static成員進行初始化;
<4> 超類成員變數按順序初始化,遞迴呼叫超類的構造方法;
<5> 子類成員變數按順序初始化,子類構造方法呼叫。
在建立物件時應注意幾個關鍵應用規則:
<1> 避免在迴圈體中建立物件,即使該物件佔用記憶體空間不大。
<2> 儘量及時使物件符合垃圾回收標準。比如 myObject = null。
<3> 不要採用過深的繼承層次。
<4> 訪問本地變數優於訪問類中的變數。
(2) 應用階段
在物件的引用階段,物件具備如下特徵:
<1> 系統至少維護著物件的一個強引用(Strong Reference);
<2> 所有對該物件的引用全部是強引用(除非我們顯示地適用了:軟引用(Soft Reference)、弱引用(Weak Reference)或虛引用(Phantom Reference)).
強引用(Strong Reference):是指JVM記憶體管理器從根引用集合出發遍歷堆中所有到達物件的路徑。當到達某物件的任意路徑都不含有引用物件時,這個物件的引用就被稱為強引用。
軟引用(Soft Reference):軟引用的主要特點是有較強的引用功能。只有當記憶體不夠的時候,才回收這類記憶體,因此記憶體足夠時它們通常不被回收。另外這些引用物件還能保證在Java丟擲OutOfMemory異常之前,被設定為null。它可以用於實現一些常用資源的快取,實現Cache功能,保證最大限度地使用記憶體你而不引起OutOfMemory。
下面是軟引用的實現程式碼:
import
java.lang. ref .SoftReference;
...
A
a = new A();
...
//
使用a
...
//
使用完了a, 將它設定為soft引用型別,並且釋放強引用
SoftReference
sr = new SoftReference(a);
a
= null ;
...
//
下次使用時
if (sr
!= null )
{
a
= sr. get ();
}
else {
//
GC由於低記憶體,已釋放a,因此需要重新裝載
a
= new A();
sr
= new SoftReference(a);
}
|
軟引用技術的引進使Java應用可以更好地管理記憶體,穩定系統,防止系統記憶體溢位,避免系統崩潰。因此在處理一些佔用記憶體較大且生命週期較長,但使用並不繁地物件時應儘量應用該技術。提高系統穩定性。
弱引用(Weak Reference):弱應用物件與軟引用物件的最大不同就在於:GC在進行垃圾回收時,需要通過演算法檢查是否回收Soft應用物件,而對於Weak引用,GC總是進行回收。Weak引用物件更容易、更快地被GC回收。Weak引用物件常常用於Map結構中。
import
java.lang. ref .WeakReference;
4.
...
5.
6.
A a = new A();
7.
...
8.
9.
//
使用a
10.
...
11.
12.
//
使用完了a, 將它設定為Weak引用型別,並且釋放強引用
13.
WeakReference wr = new WeakReference(a);
14.
a = null ;
15.
...
16.
17.
//
下次使用時
18.
if (wr
!= null )
{
19.
a = wr. get ();
20.
} else {
21.
a = new A();
22.
wr = new WeakReference(a);
23.
}
|
虛引用(Phantom Reference): 虛引用的用途較少,主要用於輔助finalize函式的使用。
虛引用(Phantom Reference)物件指一些執行完了finalize函式,併為不可達物件,但是還沒有被GC回收的物件。這種物件可以輔助finalize進行一些後期的回收工作,我們通過覆蓋了Refernce的clear()方法,增強資源回收機制的靈活性。
在實際程式設計中一般很少使用弱引用和虛引用,是用軟引用的情況較多,因為軟引用可以加速JVM對垃圾記憶體的回收速度,可以維護系統的執行安全,防止記憶體溢位(OutOfMemory)等問題的產生。
(3) 不可視階段
當一個物件處於不可視階段,說明我們在其他區域的程式碼中已經不可以在引用它,其強引用已經消失,例如,本地變數超出了其可視
的範圍。
1 . try {
2 .
Object localObj = new Object();
3 .
localObj.doSomething();
4 .
} catch (Exception
e) {
5 .
e.printStackTrace();
6 .
}
7 .
8 .
if ( true )
{
9 .
|