Java虛擬機之垃圾回收

      內存作爲程序運行時非常重要的一個系統資源,直接影響到程序的正常運行和程序性能。C/C++需要程序員顯式地分配和釋放內存,這不僅給程序員帶來工作負擔,而且還會給程序帶來內存錯誤訪問和泄露的安全隱患。Java的垃圾回收能夠自動地管理內存,不僅提高了程序開發效率,而且保證了系統的安全性。

      Java的垃圾回收機制主要的任務有兩個:1. 識別不再被使用的對象;2. 釋放不再被使用對象的內存,並歸還給程序。

        爲了識別不再被使用的對象,Java有兩種標記垃圾的方法:
1. 計數法:
        每個對象分配一個計數器,用來區分活躍對象和不再使用對象。對象每被引用一次,其計數器加1,引用消失,其計數器減1。計數器爲0時,對象被回收,其引用的所有對象的計數器都減1。
      計數法的優勢是很快被執行,程序可以不用長時間停頓,比較適合於實時程序。其劣勢是無法檢測循環引用,計數器的加減帶來額外開銷。

2. 跟蹤法:
        從根結點開始追蹤對象引用圖,遇到對象,打上標記,追蹤結束時,沒有標記的對象是無法觸及的,則可以回收。可以在程序註冊表、每一個線程堆棧中的局部變量以及靜態變量中找到根。

        由於計數法的缺陷,這種技術已經不再應用到實際Java虛擬機中。下面主要介紹基於跟蹤法的四種垃圾收集器:
1.跟蹤收集器:
        分爲標記和清除兩個階段。在標記階段,停止程序,通過遍歷引用圖,標記可觸及的對象。在清除階段,清除未被標記的對象,釋放它們的內存。
         跟蹤收集器比較簡單,同時可以收集循環引用,而且不會有計數器加減的額外代價。但是它的不足之處是,標記階段可能使程序停頓的時間過長,清除階段釋放的內存容易產生內存碎片。

2.拷貝收集器:
        爲了避免產生內存碎片,堆分成兩個區域。一個區域空間耗盡,程序中止,堆被遍歷,遇到活動對象拷貝到另一個區域連續地址空間上,內存從新的區域中分配,原來區域的內存被釋放。
        這種技術雖然避免了釋放對象時產生的內存碎片,但是任何時候只能使用堆的一半的存儲空間。同時,對於對於生命週期比較長的對象,會頻繁地在兩個區域間來回拷貝,嚴重影響系統效率。

3.壓縮收集器:
         在清除階段,把活動對象移到堆的一端,堆的另一端出現一個大的連續空閒區。由於對象的位置被移動,對象的引用需要更新。
         通過對象句柄和對象句柄表,可以使對象引用變得更簡單。對象引用不再直接指向堆中的對象,而是指向對象句柄表。對象句柄表中的對象句柄指向對象在堆中的實際位置。對象被移動後,只需要更新對象句柄,而不用修改對象引用。
        這種策略結合了跟蹤收集器和拷貝收集器的優點,代價是收集階段除了釋放不可觸及對象,還要移動活躍對象。這種策略的優勢既解決了內存碎片,又使生命週期長的對象趨向於沉於堆的底部,從而避免了這些對象的頻繁拷貝。

4. 按代收集器:
       雖然壓縮收集器結合了跟蹤收集器和拷貝收集器的優點,但是每次對象訪問都要先經過對象句柄表,才能訪問到堆中的對象。由於每次對象訪問的額外一次訪問,嚴重影響到程序性能,因此壓縮收集器並不是完勝另外兩種策略。
      按代收集器根據程序中對象生命週期的特點,將堆按代劃分爲子堆,每個子堆存儲一代對象。越年輕的一代,收集的頻率越高。一代中某對象經過幾次垃圾收集仍然存活,就會被移到年齡更高的一代中。
      不同的代中,可以採用不同的收集器。年輕代中可以採用拷貝收集器,避免生命週期長的對象頻繁來回拷貝的缺陷。年老代中可以採用跟蹤收集器或者壓縮收集器,避免了碎片整理帶來的額外代價。

      Java的垃圾回收機制雖然相對於其他語言在內存管理上有很大優勢,但是這種機制目前也存在一定的不足:
1. 降低程序性能:自動回收垃圾,需要暫停程序運行,佔用CPU資源
2. 缺乏控制:垃圾回收由虛擬機實現,程序員無法控制
發佈了34 篇原創文章 · 獲贊 22 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章