Java垃圾收集學習筆記
(1)除了釋放不再被引用的物件,垃圾收集器還要處理堆碎塊。請求分配新物件時可能不得不增大堆空間的大小,雖然可以使用的空閒空間是足夠的,但是堆中沒有沒有連續的空間放得下新物件。可能會導致虛擬機器產生不必要的”記憶體不足“錯誤。
(2)使用垃圾收集堆,有一個潛在的缺陷就是加大程式的負擔,可能影響程式的效能。因為虛擬機器需要追蹤哪些物件被正在執行的程式引用,還要動態釋放垃圾物件。
(3)程式可以呼叫System.gc()建議jvm去收集垃圾, 但是不能為垃圾回收機制指定某個物件是不是垃圾。即便呼叫了gc(),並不會馬上進行垃圾回收,甚至不一定會執行垃圾回收。所有的記憶體分配和回收許可權都在jvm,不在開發人員手裡。
可以試試:
public class RubbishRelease {
// 類的finalize方法,可以告訴垃圾回收器應該執行的操作,該方法從Object類繼承而來。
// 在從堆中永久刪除物件之前,垃圾回收器呼叫該物件的finalize方法。
public void finalize() {
System.out.println("the Object is going...");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
// 下面不斷建立物件,但是這些物件都沒有被引用
new RubbishRelease();
new RubbishRelease();
new RubbishRelease();
System.gc();
}
System.out.println("The program is over!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
執行結果:
(4)垃圾收集演算法有很多,但任何垃圾收集演算法都必須做兩件事情。首先,它必須檢測出垃圾物件
(5)區分活動物件和垃圾的兩個基本方法是引用計數和跟蹤。
(6)引用計數是垃圾收集的早期策略。在這種方法中,堆中每一個物件都有一個引用計數。一個物件被建立了,並且指向該物件的引用被分配給一個變數,這個物件的引用計數被置為1。當任何其他變數被賦值為對這個物件的引用時,計數加1。當一個物件的引用超過了生存期或者被設定一個新的值時,物件的引用計數減1。任何引用計數為0的物件可以被當作垃圾收集。當一個物件被垃圾收集的時候,它引用的任何物件計數值減1。這種方法的好處是,引用計數收集器可以很快地執行,交織在程式的執行之中。這個特性對於程式不能被長時間打斷的實時環境很有利。壞處就是,引用計數無法檢測出迴圈(即兩個或者更多的物件互相引用)。
(7)跟蹤收集器追蹤從根節點開始的物件引用圖。給追蹤過程中遇到物件以某種方式打上標記。追蹤結束時,未被標記的物件就是無法觸及的,從而被收集。基本的追蹤演算法被稱作“標記並清除”,這個名字指出垃圾收集過程的兩個階段。
(8)Java虛擬機器的垃圾收集器可能有對付堆碎塊的策略。標記並清除收集器通常使用的兩種策略是壓縮和拷貝。這兩種方法都是快速地移動物件來減少堆碎塊。
(9)壓縮收集器把活動的物件越過空閒區滑動到堆的一端,在這個過程中,堆的另一端出現一個大的連續空閒區。所有被移動的物件的引用也被更新,指向新的位置。
(10)拷貝收集器把所有的活動的物件移動到一個新的區域。在拷貝過程中,被緊挨著佈置,這樣可以消除原本它們在舊區域的空隙。即空閒區。一般的拷貝收集器演算法被稱為“停止並拷貝”。此方案中,堆被分成兩個區域,任何時候都使用一個區域。物件在同一個區域中分配直到被耗盡。此時,程式執行被中止,堆被遍歷,遍歷時遇到活動的物件被拷貝到另個區域。當停止和拷貝過程結束時,程式恢復執行。依次往復,對於指定大小的堆來說需要兩倍大小的記憶體,由於任何時候都只使用其中的一半,這就是該方法帶來的代價。
(11)按代收集:根據物件的存活週期(一次垃圾收集為一個週期)的不同將記憶體劃分為幾塊。一般是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。在新生代中,每次垃圾收集時都發現有大批物件死去,只有少量存活,那就選用拷貝演算法,只需要付出少量存活物件的拷貝成本就可以完成收集。而老年代中因為物件存活率高、沒有額外空間對它進行分配擔保,可以使用“標記並清除”演算法。
(12)終結方法(finalize),這個在上面第3點也有提到:這個方法是垃圾收集器在釋放物件前必須執行。這個可能存在的終結方法使得任何Java虛擬機器的垃圾收集器要完成的工作更加複雜。因為終結方法可能“復活”了某些不再被引用的物件(本身或者其他物件)。
(13)堆中的每一個物件都有三種狀態之一:可觸及的、可復活的以及不可觸及的。可觸及狀態好理解。關於可復活狀態:它在從根節點開始的追蹤圖中不可觸及,但是又可能在垃圾收集器執行某些終結方法時觸及。不僅僅是那些聲明瞭finalize方法的物件,而是所有的物件都要經過可復活狀態。而不可觸及狀態標誌著不但物件不再被觸及,而且也不可能通過任何終結方法復活。不可觸及的物件不再對程式的執行產生影響,可自由地回收它們佔據的記憶體。
(14)物件的強,軟,弱,虛引用。
強引用:如果一個物件具有強引用,垃圾回收器絕不會回收它。當記憶體空間不足,JVM寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會考隨意回收具有強引用的物件來解決記憶體不足的問題。
軟引用:如果一個物件具有軟引用。如果記憶體空間足夠。垃圾回收器不會回收它。如果記憶體不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。
弱引用:如果一個物件具有弱引用。當垃圾回收器發現只具有弱引用物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現只具有弱引用的物件。
虛引用:虛引用不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。