Hotspot垃圾回收
Hotspot VM 使用分代回收算法(Generational Collector)
??
GC堆的分代:
(1)Young Generation(年青代):大多數對象
?????? Eden區+Survivor_1區+Survivor_2區
(2)Old Generation(年長代):年青代的幸存對象(若干次回收后剩下的對象)+大對象
(3)Permanent Generation(持久代):元信息對象(ClassObject,Method...)
??
快速分配(Fast Allocation)????
bump-the-pointer:尾部指針(指向之前已分配對象的尾部)
???? 分配時比較(空間尾部和尾部指針的差值)剩余空間大小
???
線程局部分配緩沖(Thread-Local Allocation Buffers):
???? 每個線程有一個獨立的分配緩沖(Eden區的一部分),再使用bump-the-pointer完成分配
??
??
1.串行回收(serial collector)
使用串行收集器時,對年幼代和年長代的收集都采用串行、STW方式進行。也就是說收集任務同時只使用一個CPU,而且在收集任務執行期間,應用程序的執行被中止。
(1)年青代
????首先把 eden中的活動對象復制到空閑的 survivor space中,即圖中標為 To的區域,其中的一些大對象會被直接復制到老年代中。另一個 survivor space(標為 From)中仍然年輕的對象同樣會被復制到 To 中,而其中的一些年老的對象會被復制到老年代中。注意,在向 To中復制對象的時候,如果 eden或 From中還有一些對象沒有被復制的時候 To已經被填滿了,那么 eden和 From中的這些對象不管它經歷過幾次新生代垃圾收集都會被直接復制到老年代中。eden和From 中剩余的對象就成為了垃圾,它們所占用的內存將會被釋放。
??? 經過一次新生代垃圾收集后,eden和 From都是空閑的,只有 To中含有活動的對象,此時,將交換兩個 survivor space,即 From 被標識為 To,To則為 From
(2)年長代
在串行垃圾收集器中,老年代和永生代使用標記清掃壓縮(mark-sweep-compact)算法。在標記階段,垃圾收集器標識出哪些對象是活動對象。清掃階段則標識出垃圾對象。最后垃圾收集器執行平移壓縮,把活動對象向老年代或永生代的一端平移,這樣老年代或永生代的另一端就是一塊連續的空閑內存。
??
2.并行回收(parallel collector)
目前,許多Java應用的運行平臺大都包含很多物理內存和多個CPU。并行收集器,也被稱作吞吐量收集器,被開發出來的主要目的就是為了充分利用CPU資源,而不是只讓一個CPU去做垃圾收集而其他CPU卻被閑置。
(1)年青代
和串行收集器相比,并行收集器采用了大致相同的年幼代收集算法,只是執行的是其并行版本而已。對年幼代的收集雖然仍基于拷貝技術、采用STW方式進行,但收集工作是并行展開的,使用了多個CPU,這就降低了垃圾收集開銷,從而提高了應用程序的吞吐量。下圖展示了并行收集器和串行收集器在執行年幼代收集時到底有何不同:
(2)年長代
和串行收集器一樣,并行收集器對年長代的收集同樣基于標記-清理-壓縮算法,同樣采用串行、STW方式進行。
??
3.并行緊縮回收(parallel compacting collector)
????? (1)年青代
????? 同并行回收的年青代
????? (2)年長代
????? 平行壓縮垃圾收集器在老年代和永生代中并行地執行 stop-the-world式的垃圾收集,并進行平移壓縮。這種垃圾收集器的垃圾收集過程分為三個階段。在標記階段,垃圾收集器首先會把每個代邏輯上劃分為固定大小的區域 ( region ),這樣應用程序中的強可達對象就分布于不同的垃圾收集線程中,垃圾收集線程可以并行地標記活動對象。一旦一個對象被標記為活動的,垃圾收集器會更新這個對象所在區域的數據結構,例如對象的大小及位置。
??????匯總階段的的操作是針對區域( region )的,而不是對象。由于之前進行的垃圾收集操作,每個代的左端活動對象的密度較高。從這種活動對象密度較高的區域中壓縮回收空間所需的開銷很大,但釋放回收的內存空間卻很有限,這樣對這些區域進行壓縮垃圾收集是很不劃算的。因此在匯總階段垃圾收集器從代的左端開始檢查每個區域的活動對象的密度,直到發現這樣一個區域,壓縮回收這個區域以及這個區域右邊的區域中的空間開銷很小。當找到這樣一個區域后,這個區域左邊的區域會稱為 dense prefix,而且不會有對象復制到這些區域中。對這個區域右邊的區域進行壓縮,并回收垃圾對象所占用的內存空間。匯總階段會計算并保存壓縮后的區域中對象的新地址。注意,當前匯總階段的實現是串行的,當然匯總也可以實現為并行的,但相對于性能匯總階段的并行不及標記壓縮階段來得重要。
??????在壓縮階段,垃圾收集線程使用匯總階段生成的數據標識出可以進行壓縮填充的區域,這些線程并行地進行數據復制,從一個區域復制到另一個區域。這樣就使堆的一端有大量的活動對象,而另一端則是一塊大的空閑內存區域。?
?? ??
4.并發標記清理回收(Concurrent Mark-Sweep Collector)CMS
????? (1)年青代
????? 同并行回收的年青代
????? (2)年長代
??????老年代垃圾收集的大部分操作是與應用程序一起并發地執行的。
? ?? ?CMS垃圾收集器進行老年代垃圾收集時首先會進行初始標記,在初始標記階段 cms垃圾收集器會標識應用中的強可達對象,這個階段會暫停應用的執行。接著 cms垃圾收集器進行并發標記,并發標記從初始標記階段標識的活動對象開始,遞歸地遍歷并標識這些對象中引用的強可達對象。由于并發標記時應用程序也在執行,運行中的應用程序可能會更新對象的引用,所以并發標記不能標識出內存中的全部活動對象。為了解決這個問題應用程序會被再次暫停,cms垃圾收集器再次訪問在并發標記過程中引用被修改過的對象,這稱為重新標記。為了提高重新標記的效率,多個線程會并行地執行重新標記。??
??????重新標記執行完后,堆中所有的活動對象都被標識出來了,接下來的并發清掃階段垃圾收集器會回收堆中的垃圾對象
????? 像重新標記這樣的任務會增加垃圾收集的工作量,這也增加了垃圾收集的開銷,所以降低暫停時間是通過增加垃圾收集的開銷來獲得的。
????? cms 垃圾收集器是唯一一個不進行緊縮的垃圾收集器,當 cms釋放了垃圾對象占用的內存后,它不會把活動對象移動到老年代的一端。
這樣做減少了 cms垃圾收集器進行垃圾收集花費的時間,但由于空閑的內存不是連續的,無法使用一個簡單的指針來標識出下一個可以用來分配的空閑內存。cms垃圾收集器使用了一個空閑內存鏈表把空閑的內存鏈接在一起,每次分配內存的時候,垃圾收集器會在這個鏈表中查找出第一個能滿足分配需要的空閑內存,所以從老年代中分配對象是十分昂貴的。這同樣會影響到新生代垃圾收集,因為老年代中的大部分對象是在新生代垃圾收集的時候從新生代晉升為老年代的。
?
?? ?? cms垃圾收集器的另一個缺點是它需要的堆空間比其他的垃圾收集器要大。由于在標記階段應用程序的執行并沒有被暫停,應用程序執行過程中會繼續申請內存,這樣在垃圾收集的過程中老年代處于增長狀態。另外,雖然垃圾收集器保證能夠識別所有的活動對象,但有一些對象在標記過程中成為了垃圾,而這些對象在此次垃圾收集過程中并不會被回收,要等到下次垃圾收集才會被回收。這樣的對象被稱為?floating garbage?。
?
? ?? ?由于不執行壓縮所以 cms 垃圾收集器很容易引起內存碎片。為了解決這個問題,cms會追蹤常用的對象大小,預估內存需求,有時會分隔或合并空閑內存塊。
?
??????與其它垃圾收集器不同,cms垃圾收集器不會在老年代被填滿的時候執行垃圾收集。相反,它會在老年代被填滿之前盡早地執行垃圾收集,否則這就與串行或并行垃圾收集器一樣會造成應用長時間地暫停。為了避免這種情況,cms會根據一些統計信息來決定執行垃圾收集的時機或者當老年代的使用達到某個伐值的時候也會啟動 cms垃圾收集,通過命令行參數 -XX:CMSInitiatingOccupancyFraction = n設置初始伐值,其中 n是老年代大小的百分比,當來年代的使用達到這個伐值時 cms會啟動,n默認為 68。
?
??? ??總體來看,與平行垃圾收集器相比,cms減少了執行老年代垃圾收集時應用暫停的時間,但卻增加了新生代垃圾收集時應用暫停的時間、降低了吞吐量而且需要占用更大的堆空間
??
??
??
Hotspot JVM中的垃圾回收?
????
???
總結
以上是生活随笔為你收集整理的Hotspot垃圾回收的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Makefile分析
- 下一篇: VirtualAlloc和Virtual