一、四種引用方式1.1 強引用1.2 軟引用(SoftReference)1.3 弱引用(WeakReference)1.4 虛引用(PhantomReference)
二、如何判斷對象是垃圾2.1 引用計數(shù)法2.2 根可達性分析
三、垃圾回收算法3.1 標記-清除(mark-sweep)3.2 標記-整理(mark-compact)3.3 標記-復制(mark-copy)
四、垃圾收集器4.1 分類及特點簡述4.1.1 串行4.1.2 吞吐量優(yōu)先4.1.3 響應(yīng)時間優(yōu)先4.2 串行垃圾回收器詳述4.2.1 Serial4.2.2 Serial-Old4.2.3 流程圖4.3 吞吐量優(yōu)先垃圾回收器詳述4.3.1 JVM相關(guān)參數(shù)4.3.2 流程圖4.4、響應(yīng)時間優(yōu)先垃圾回收器詳述4.4.1 JVM相關(guān)參數(shù)4.4.2 流程圖4.3.3 CMS的特點
五、G1垃圾回收器5.1 相關(guān)JVM參數(shù)5.2 特點5.3 G1新生代垃圾回收5.4 G1老年代垃圾回收
一、四種引用方式
1.1 強引用
只有所有 GC Roots對象都不通過【強引用】引用該對象,該對象才可以被回收。
1.2 軟引用(SoftReference)
- 僅有軟引用引用該對象時,在垃圾回收后,若內(nèi)存仍不足時再次發(fā)出的垃圾回收,回收軟引用對象
- 可以配合引用隊列來釋放軟引用自身
1.3 弱引用(WeakReference)
- 僅有弱引用引用該對象時,每次發(fā)生垃圾回收的時候,無論內(nèi)存是否充足都會回收掉這部分對象
- 可以配合引用隊列來釋放軟引用自身
1.4 虛引用(PhantomReference)
- 必須配合引用隊列來使用,主要配合ByteBuffer的使用,被引用對象回收時,會將虛引用存入隊列,由Reference Handler線程調(diào)用虛引用相關(guān)方法釋放直接內(nèi)存
二、如何判斷對象是垃圾
2.1 引用計數(shù)法
某個對象只要有一處引用關(guān)系,該對象的引用次數(shù)就加1,如果一個對象的引用次數(shù)為0,則說明該對象是垃圾。
優(yōu)勢:實現(xiàn)簡單,效率較高
弊端:如果有一對對象之間形成了相互引用,但是這兩個對象都已經(jīng)沒有被其它對象所引用了,正常情況下,這一對對象應(yīng)該被作為垃圾回收掉,但是因為形成了相互引用導致無法被回收。
2.2 根可達性分析
通過GC Root對象開始向下尋找,尋找不到的對象即說明沒有被引用,那么這些沒有被引用的對象被認定為垃圾。
目前,如下對象可以作為GC Root對象:
三、垃圾回收算法
根據(jù)前面的描述,知道了哪些對象是垃圾需要被回收,那么回收是按照什么樣的算法呢?
3.1 標記-清除(mark-sweep)
很好理解,即在GC的放生時候,先對所有對象進行根可達性分析,借此標記所有的垃圾對象;所有對象標記完畢之后會進行清理操作。
因此,總體來說,就是先標記再清除。
弊端;標記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,碎片太多可能會導致程序運行過程中需要分配較大對象時,無法滿足分配要求導致GC操作。
3.2 標記-整理(mark-compact)
該回收算法操作過程基本等同于標記-清除算法只不過,第二步有點區(qū)別,該種方式會在清除的過程中進行整理操作,這是最大的不同。
優(yōu)勢:最終不會出現(xiàn)若干空間碎片而導致的空間浪費。
弊端:在整理過程中帶來的計算不可小覷。
3.3 標記-復制(mark-copy)
該種方式與前兩種有較大的區(qū)別:
該種方式會將存儲區(qū)分成兩個部分,分別為From、To,其中From區(qū)域中可能存在著對象,而To區(qū)域始終為空,用做下一次接受數(shù)據(jù)做準備。
分別有兩個指針指向這兩個區(qū)域:From-Pointer、To-Pointer,
優(yōu)點:這種算法非常適合早生夕死的對象
缺點:始終有一塊內(nèi)存區(qū)域是未使用的,造成空間的浪費。
四、垃圾收集器
垃圾收集器,就是實現(xiàn)了上述三種垃圾回收算法的具體實現(xiàn)
在Java中不同的“代”所保存的對象特點都不一樣,因此Java在垃圾回收的選擇上并沒有偏愛一種,只采用一種算法方式,而是根據(jù)不同“代”對象的特點,采取不同的垃圾回收器(垃圾回收算法)。
4.1 分類及特點簡述
4.1 串行
特點:
- 單線程
- 堆內(nèi)存小,適合于個人電腦
4.2 吞吐量優(yōu)先
特點:
- 多線程
- 堆內(nèi)存較大,適合多核CPU
- 讓單位時間內(nèi),STW的時間最短,即一段時間中,垃圾回收的時間與總運行時間的占比,占比越小越好,說明大多數(shù)時間都在處代碼邏輯
4.3 響應(yīng)時間優(yōu)先
特點:
- 多線程
- 堆內(nèi)存較大,適合多核CPU
- 盡可能讓單次STW時間最短,只是單單的追求每次垃圾回收時間短即可,并不在意一段時間內(nèi)發(fā)生了多少次垃圾回收。
4.2 串行垃圾回收器詳述
JVM開關(guān):-XX:+UseSerialGC = Serial + SerialOld
4.2.1 Serial
- 工作在新生代
- 采用復制算法
- 單線程
4.2.2 Serial-Old
- 老年代
- 采用標記-整理算法
- 單線程
4.2.3 流程圖
4.3 吞吐量優(yōu)先垃圾回收器詳述
JDK1.8默認開啟的,使用的算法本身與Serial是一致的,只是處理線程不一樣而已。
4.3.1 JVM相關(guān)參數(shù)
- -XX+UseParallelGC
- 工作在新生代
- 復制算法
- -XX+UseParallelOldGC
- 工作在老年代
- 標記-整理算法
- -XX:+UseAdaptiveSizePolicy
- 動態(tài)調(diào)整Eden與Survivor的比例
- -XX:GCTimeRatio=ratio(默認為99)
- 占比 = 1/(1+radio)
- 表示希望在當前運行總時間中,GC的時間與總時間的占比小于等于上面的公式值。
- -XX:MaxGCPauseMills=ms(默認200ms)
- 單次垃圾回收的時間
- -XX:ParallelGCThread=n
- 指定并行的垃圾處理線程數(shù),默認為CPU核數(shù)
- 因此在垃圾回收的過程中可能會導致CPU一下子跑滿
4.3.2 流程圖
4.4、響應(yīng)時間優(yōu)先垃圾回收器詳述
4.4.1 JVM相關(guān)參數(shù)
- -XX:+UseParNewGC
- 工作在新生代
- -XX:+UseConcMarkSweepGC
- 工作在老年代,若垃圾回收失敗時,則會退回到SerialOld垃圾回收。
- 采用標記清除算法;
- 可以并發(fā)執(zhí)行,即在某些垃圾回收階段,垃圾回收線程可以與用戶線程一起執(zhí)行。
- -XX:ConGCThread=n
- 指定在并發(fā)收集的過程中,可以使用n個線程處理垃圾回收
- -XX:CMSInitiationOccupancyFraction=precent
- 在下面的分析中,會介紹到浮動垃圾的概念,因此為了更加及時的清除浮浮動垃圾,在老年代的空間占用達到了Precent的時候,就會觸發(fā)老年代的垃圾回收。
- -XX:+CMSScavengeBeforeRemark
- 因為老年代的CMS涉及到重新標記,重新標記就是在判斷得到的垃圾對象是否又被重新引用。那么CMS會從新生代開始,使用根可達性分析(因為根可達性分析是不可逆的,也就是說無法通過某個對象直接查看其是否被引用,就好像二叉查找樹一樣,必須先從根對象開始往下尋找,看是否能找到該對象),而新生代中的對象較多,一個一個的從新生代對象進行更可達性分析,勢必會拖慢響應(yīng)分析,因此在重新標記之前,若改參數(shù)開關(guān)已打開,會先進行一次新生代的垃圾回收。
- -XX:ParallelGCThreads=n
- 指定并行的垃圾處理線程數(shù),默認為CPU核數(shù)。
4.4.2 流程圖
上圖是:CMS垃圾回收器在老年代GC的工作流程圖:
4.3.3 CMS的特點
五、G1垃圾回收器
5.1 相關(guān)JVM參數(shù)
- -XX:UseG1GC
- 開啟使用G1垃圾回收器
- -XX:G1HeapRegionSize=n(默認是2048)
- 指定整個堆被劃分成N個Region,因此每個Region的大小為堆空間 / N,但是有一條硬性規(guī)定,即每個Region的大小必須是 2 的整數(shù)倍。
- 兩個初始Region數(shù)公式:
- 新生代的Region個數(shù) = 5% * N
- 老年代的Region個數(shù) = 95% * N
- 而對于新生代而言,在以往,新生代中進一步被劃分為Eden區(qū)、Survivor(From、To)區(qū),且三者的比值依然為:8:1:1,這樣的劃分在G1中同樣的適用,只不過這樣的劃分不會出現(xiàn)在同一個Region中,而是Eden在某個獨立的新生代Region中,F(xiàn)rom、To都各占一個獨立的Region。
- -XX:InitaingHeapOccupancyPercent=45%
- 老年代的Region個數(shù)達到n%,即會觸發(fā)老年代垃圾回收。
- –XX:G1MixedGCuntTarget=n
- 在混合模式下的垃圾回收,在并發(fā)清理階段分為n次進行
- -XX:MaxGCPauseMillis=ms
- 指定的垃圾回收最長時間,如果時間很短,G1就只會回收那些回收價值最高的Region,這是為了能夠達到這個暫停目標。
5.1 特點
- 全年代垃圾回收器
- G1負責全年代的垃圾回收,不像前面幾種垃圾回收器,如:Serial(新生代)需要配合SerialOld(老年代)、CMS(老年代)需要配合ParNew(新生代)
- G1對堆的劃分方式不一樣
- 在G1之前,堆空間被“切”成兩份,分別是:新生代、老年代,
- 而G1并沒有按照傳統(tǒng)方式去劃分,G1將整個堆空間劃分為一個一個相同大小的Region,而每個Region要么屬于新生代,要么屬于老年代,而每個新生代或老年代的Region在物理上不是一塊連續(xù)的物理地址。而每個Region也不一定在整個項目周期中完全屬于新生代或老年代而是處于一種動態(tài)變化的過程中。
- 每次回收某個代并不會全部進行掃描回收
- 回收價值:這是每個Region中帶有的屬性,Calc(對象的存活率、回收預計耗時、回收效果…)
- 而當進行回收時,會優(yōu)先選取回收價值高的Region,從而減少垃圾回收的區(qū)域。
- 大對象存儲
- 在以往分代模型中,當某個大對象進入內(nèi)存時,如果整個新生代在垃圾清理之后依然無法使用,但是老年代卻有足夠的空間,此時該大對象會直接進入到老年代;
- 而在G1中并沒有使用上述的方式存儲過大的對象,而是大對象會被安排到了一個叫Humongous的Region中,在發(fā)生新生代或者老年代垃圾回收時,都會順帶清理Humongous的Region。
5.2 G1新生代垃圾回收
經(jīng)過上面的文字分析,新生代的Region個數(shù)為所有Region個數(shù)的5%;這個數(shù)值其實是很小的,那么當新生代Region不夠用的時候,JVM會劃分更多的Region個數(shù)給新生代;
當新生代的Region個數(shù)占比所有Region個數(shù)超過 60%時,就會進行一次新生代的垃圾回收。
新生代垃圾回收會造成STW。
具體的垃圾回收算法同其它幾個新生代垃圾回收器一樣,新生代都使用復制算法。
5.3 G1老年代垃圾回收
老年代垃圾回收觸發(fā)機制與參數(shù)-XX:InitaingHeapOccupancyPercent有關(guān)。
但是需要注意的是:這一次的老年代回收,其實是一次混合垃圾回收,會同時清理新生代、老年代、Humongous。
與新生代回收算法一致,依然使用復制算法,但是垃圾回收的過程等同于老年代響應(yīng)時間優(yōu)先的CMS方式
流程分為: