深入理解Java虚拟机 第2版 周志明著(三)
第3章 垃圾收集器與內存分配策略
3.1 如何判定對象是“活著”或者“死去”?
-
引用計數法:給對象中添加一個引用計數器,每當有一個地方引用它時,計數器就加1,當引用失效時,計數器值就減1,任何時刻計數器為0的對象就是不在被使用的對象。
缺點:很難解決循環引用問題。 -
可達性分析算法:通過一系列的稱為"GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,這個對象就是不可用的。
可作為GC Roots的對象有:
1.虛擬機棧(棧幀中的本地變量表)中引用的對象。
2.方法區中類靜態變量屬性引用的對象。
3.方法區中常量引用的對象。
3.2 引用
Java中引用的定義:如果reference類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱這塊內存代表著一個引用。
在定義之下對象只有被引用或者沒有被引用兩種狀態,但是我們希望有一些中間態的對象,當空間足夠的時候保留在內存中,如果內存空間在進行垃圾收集后還是非常緊張的話可以拋棄這些對象。所以Java中才有了,強引用,軟引用,弱引用,虛引用。
3.3 垃圾收集算法
首先標記出所有需要回收的對象,標記完成后統一回收所有被標記的對象。
缺點:效率低,會產生大量不連續的內存碎片,可能會導致需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發一次GC。
將可用內存的容量劃分為大小相等的兩塊,每次只用其中一塊,當這一塊的內存用完了,就將還活著的對象復制到另外一塊上,然后再把使用過的這塊清空。
缺點:內存縮減為原來的一半。
大部分虛擬機都是用這種算法,但是內存比例是按照8:1:1來分配的。即將內存分為較大的Eden空間,和兩塊較小的From Survivor和To Survivor空間,每次使用Eden和From Survivor。當回收時,將Eden和From Survivor中還存活的對象一次性的復制到To Survivor中,最后清理掉Eden和From Survivor。當To Survivor內存空間不夠用來收集存活的對象時,需要依賴老年代進行分配擔保。
首先標記出所有需要回收的對象,然后讓所有存活的對象都向一端移動,然后直接清理掉邊界以外的內存。
把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。
3.4 垃圾收集器
Serial收集器
單線程收集器,它在進行垃圾收集時必須暫停其他所有的工作線程,直到它收集結束。它是虛擬機運行在Client模式下的默認新生代收集器。
算法:復制算法
ParNew收集器
就是Serial收集器的多線程版本,目前只有它可以和CMS收集器配合工作。單CPU的環境中效果一定不會有Serial收集器效果好,雙CPU也不一定能保證。但是CPU越多效果越好。默認開啟的收集線程數與CPU數量相同。
算法:復制算法
Parallel Scavenge收集器
新生代的收集器,又是并行的多線程收集器,特點是它的關注點與其他的收集器不同,其他的收集器的關注點是盡可能的縮短垃圾收集時間用戶線程的停頓時間。而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量。吞吐量是CPU用戶運行代碼的時間與CPU總消耗時間的比值。公式:
吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)
Parallel Scavenge收集器與ParNew還有一個重要區別就是Parallel Scavenge收集器可以設置自適應的調節策略。Parallel Scavenge收集器架構中是有收集器來進行老年代收集的,但是這個收集器的實現和Serial Old非常接近
Serial Old收集器
Serial Old是Serial收集器的老年代版本,同樣是單線程收集器,這個收集器的主要意義在于給Client模式下的虛擬機使用,一般作為CMS收集器的后備預案。
算法:標記 - 整理
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本。在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge收集器加Parallel Old收集器的組合。
算法:標記 - 整理
CMS收集器
老年代收集器,CMS收集器是一種以獲取最短回收停頓時間為目標的收集器。優點,并發收集,低停頓。
算法:標記 - 清除
運作過程分為:初始標記,并發標記,重新標記,并發清除4個步驟。其中初始標記和重新標記這兩個步驟仍然需要Stop The World。
初始標記:僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快、
并發標記:就是進行CG Roots Tracing(找引用鏈)的過程。
重新標記:是為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄。這個階段的停頓時間一般會比初始標記的時間長,但是遠低于并發標記的時間。
并發清除:回收垃圾。
缺點:1.雖然不會導致用戶線程停頓,但會因為占用了一部分線程導致應用程序變慢,總吞吐量降低。2.CMS無法處理浮動垃圾(并發清除中用戶線程還在運行,也就還會有新的垃圾出現,這一部分垃圾沒有被標記過,無法在當次收集中處理掉,這一部分垃圾稱為浮動垃圾)。正是因為清除的時候用戶線程還在運行,所以需要預留一部分空間給用戶線程在這期間產生的垃圾使用,如果預留的空間不夠用,虛擬就就會臨時啟用Serial Old收集器來重新進行老年代的垃圾收集,停頓時間就很長了。3.由于算法實現的原因會產生大量的空間碎片。
G1收集器
面向服務端的收集器,優點:并行與并發,分代收集,空間整合,可預測停頓。它將整個java堆劃分為多個大小相等的獨立的區域(Region),新生代和老年代不再是物理隔離,他們都是一部分Region(不需要連續)的集合。
運作過程分為:初始標記,并發標記,最終標記,篩選回收4個步驟。
3.5 內存分配
1.對象優先在Eden分配(當Eden沒有足夠空間時,虛擬機發起一次Minor GC)。
2.大對象直接進入老年代。
3.長期存活的對象進入老年代(默認年齡15)。
4.如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或者等于該年齡的對象可以直接進入老年代。
5.空間分配擔保:在發生Minor GC之前,虛擬機會檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果大于,那Minor GC安全。如果小于,接著查看HandlePromotionFailure的值是否允許失敗,如果設置允許失敗,會檢查老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,嘗試進行一次Minor GC。如果小于或者HandlePromotionFailure的值設置不允許,那這時需要進行一次Full GC。
總結
以上是生活随笔為你收集整理的深入理解Java虚拟机 第2版 周志明著(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【第64期】豆瓣9.8分,周志明的《凤凰
- 下一篇: 《深入理解Java虚拟机》-周志明(转)