標籤:java虛擬機器
一、垃圾收集
Java虛擬機器的堆裡存放著正在啟動並執行java程式所建立的所有對象。當一個對象不再被程式引用時,它所使用的堆空間可以被回收,以便後續的新對象所使用。垃圾收集器必須能夠斷定哪些對象是不再被引用的,並且能夠把它們所佔據的堆空間釋放出來。在釋放不再被引用的對象的過程中,垃圾收集器運行將要被釋放的對象的終結方法(finalizer)。
除了釋放不再被引用的對象,垃圾收集器還要處理堆片段。在一個虛擬記憶體系統中,增長的堆所需要的額外分頁(或交換)空間會影響運行程式的效能。
垃圾收集
垃圾收集演算法要做兩件事情
1)檢測出垃圾對象
2)回收垃圾對象所使用的堆空間並還給程式
垃圾檢測通常通過建立一個根對象的集合并且檢查從這些根對象開始的可觸及性來實現。如果正在執行的程式可以訪問到的根對象和某個對象之前存在引用路徑,這個對象就是可觸及的。對於程式來說,根對象總是可以訪問的。從這個根對象開始,任何可以被觸及的對象都被認為是使用中的物件。無法被觸及的對象被認為是垃圾,因為它們不在影響程式的執行。 java虛擬機器的根對象集合根據實現而不同,但是總會包含局部變數中的對象引用和棧幀的運算元棧(以及類變數中的對象引用)。另一個根對象的來源是被載入的類的常量池中的對象引用,比如字串。還有一個來源是傳遞到本地方法中的,沒有被本地方法釋放的對象引用。另一個潛在的根對象的來源是,java虛擬機器運行時資料區中從垃圾收集器的堆中分配的部分。 區分使用中的物件和垃圾的兩個基本方法是引用計數和跟蹤。 引用計數垃圾收集器通過為堆中的每一個對象儲存一個計數來區分使用中的物件和垃圾對象。這個計數記錄下了對那個對象的引用次數。 跟蹤垃圾收集器實際上追蹤從根結點開始的引用圖。在追蹤中遇上的對象以某種方式打上標記,當追蹤結束時,沒有被打上標記的對象就是被斷定是不可觸及的,可以被當作垃圾收集。 java程式的退出有兩種機制:一種是程式運行完成,自然退出.另一種是拋出異常或者錯誤退出. //因此當主線程啟動一個子線程,並且主線程以以上任何一種方式退出後. 子線程是否繼續執行取決於這個線程是使用者線程還是守護線程.如果該子線程是使用者線程,該子線程會繼續執行. 如果該子線程是守護線程,該子線程會終結.// //這裡寫得有問題 應該是java程式啟動一個線程,並且java程式以以上任何一種方式退出後. 在主線程退出,在jvm退出之前.jvm會判斷當前的進程. 如果在沒有使用者進程(有或沒有守護線程)的情況下,jvm會退出該線程是否繼續執行取決於這個線程是使用者線程還是守護線程.如果該線程是使用者線程,該線程會繼續執行. 如果該線程是守護線程,該線程會終結.
引用計數
堆中每一個對象都有一個引用計數。當一個對象被建立了,並且指向該對象的引用被分配給一個變數,這個對象的引用計數被置為1.當任何其他變數被賦值為對這個對象的引用時,計數加1。當一個對象的引用超過了生存期或者被設定一個新的值時,對象的引用計數減1.任何引用數為0的對象可以被當作垃圾收集。當一個對象被垃圾收集的時候,它引用的任何對象計數值減1.在這種方法中,一個對象被垃圾收集後可能導致後續其他對象的垃圾收集工作。
好處是,垃圾收集工作可以很快執行。缺點是,無法檢測出兩個或者更多個物件循環參考的情況。
跟蹤收集器
跟蹤從根結點開始的對象引用圖。在追蹤的過程中遇到的對象以某種方式打上標記。當跟蹤結束時,未被標記的對象就知道是無法觸及的,從而可以被收集。(標記並清除)
堆片段收集
標記並清楚收集器使用兩種策略:壓縮和拷貝,來快速移動對象來減少堆片段。
壓縮收集器將活動的對象移動到堆的一端。堆的另一端出現一個大的連續空閑區。所有被移動的對象的引用也會被更新,指向新位置。
拷貝收集器把所有的使用中的物件移動到一個新的位置。在拷貝過程中,它們被緊挨著排放,可以消除原本在舊地區的空隙。
一般的拷貝收集器演算法為“停止並拷貝”。在這個方案中,堆被分為兩個地區,任何時候都只能使用其中一個。
按代收集的收集器
拷貝收集器的缺點:
1)大多數程式建立的大部分對象都具有很短的生命週期。
2)大多數程式都建立一些具有非常長生命週期的對象。
拷貝收集器浪費效率的一點是,它們每次都把生命週期長的對象來回拷貝,消耗大量的時間。
按代收集器通過把對象按照壽命來分組來解決這個效率底下的問題,更多地收集哪些那些短暫出現的年幼對象。
堆被分成多個子堆,每個堆為一代。最年幼的堆進行最頻繁的垃圾收集。如果一個相對年幼的對象經曆了好幾次垃圾收集後仍然存在,那麼這個對象就成長成壽命更高的一代,轉移到另一個子堆。
大範圍的垃圾收集會佔用大量的資源和時間,可能會導致暫停和無法滿足即時系統的要求。因此,使用漸進式的收集演算法。
火車演算法是為了在成熟對象空間提供限度時間的漸進收集。
車廂,火車和火車站
火車演算法把成熟對象空間劃分為固定長度的記憶體塊,演算法每次在一個塊中單獨執行。每個塊屬於一個集合。
塊被叫車廂,集合被叫做火車,成熟對象空間是火車站。火車被排序,塊被附加到火車的尾部。
這種方式表示出了成熟對象空間內所有塊的總體排序。
車廂收集
火車演算法執行的時候,要麼收集最小數字火車中的最小數字車廂,要麼收集整個最小數字火車。如果整個火車都是垃圾對象,那麼整個火車都被收集。否則,收集最小數字車廂。
收集最小數字車廂時,如果發現該車廂內部有被其他車廂引用對象則會轉移到引用的車廂,如此迴圈,最後收集整個車廂。
收集最小數字火車時,如果發現該火車內有被其他火車引用對象則會轉移到引用的火車,如此迴圈,最後收集整個火車。
記憶集合和流行對象
為了促進收集過程,火車演算法使用了記憶集合。一個記憶集合是一個資料結構,包含所有對一節車廂或者一列火車的外部參考。一個空的記憶集合表明車廂或者火車中的對象都不再被車廂或者火車外的任何變數引用,可以被垃圾收集。
深入理解Java虛擬機器