最近看完了《深入理解Java虛擬機(jī)》的自動(dòng)內(nèi)存管理章節(jié),覺得有必要稍微總結(jié)一下。實(shí)際上我覺得這本書里面有很多地方都只是講了個(gè)大概,就比如這一章節(jié)中對(duì)7種垃圾回收器的介紹就僅僅是介紹而已,實(shí)現(xiàn)的細(xì)節(jié)都沒有講到。當(dāng)然了,這本身也因?yàn)镚C是一個(gè)復(fù)雜的體系,這么薄薄的一本書也不可能講的完。
但是看書呢,總有個(gè)問題,就是看了就忘,所以為了防止自己忘記,就做了這么一個(gè)大綱,然后記上筆記,等到以后復(fù)習(xí)的時(shí)候,就按照這個(gè)大綱回憶。

食用方法:
- 閱讀《深入理解Java虛擬機(jī)》
-
過段時(shí)間開始回憶,點(diǎn)擊這里查看答案
截屏2019-10-22下午10.42.52.png截屏2019-10-22下午10.42.52.png
特別注意
- 《深入理解Java虛擬機(jī)》這本書的GC部分本身已經(jīng)是總結(jié)了,所以這個(gè)算是總結(jié)的總結(jié)
- 沒有《深入理解Java虛擬機(jī)》中的4種垃圾回收算法和7種垃圾收集器的總結(jié),因?yàn)樗麄儽旧韮?nèi)容就很少,而且CMS和G1收集器講的也不清楚
- 隨后加了個(gè)dalvik和hotspot對(duì)比的總結(jié),具體dalvik的實(shí)現(xiàn)推薦大家去看老羅的文章,art的還沒來得及看
文字版
可能xmind這個(gè)軟件需要錢,那就提供一個(gè)文字版吧
GC ROOTS有哪些
- 虛擬機(jī)棧中引用的對(duì)象
- 方法區(qū)的常量和靜態(tài)變量引用的對(duì)象
- 本地方法引用的對(duì)象
四種引用及垃圾回收器對(duì)它們的回收策略
- 強(qiáng)引用。一般的引用都是強(qiáng)引用,垃圾回收器不會(huì)隨意回收強(qiáng)引用
- 軟引用。當(dāng)內(nèi)存不足時(shí),垃圾回收器會(huì)把軟引用放入可回收范圍等待回收
- 弱引用。弱引用的對(duì)象只能活到下一次回收
- 虛引用。虛引用不能獲取對(duì)象,僅僅可以再對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知
對(duì)象不可達(dá)后,就真正死亡了嗎?虛擬機(jī)是如何執(zhí)行finalize方法的
對(duì)象被標(biāo)記為不可達(dá)后,會(huì)被檢查是否重寫了finalize方法以及有沒有執(zhí)行過finalize方法。如果有重寫并且沒有執(zhí)行過的話,虛擬機(jī)會(huì)把這些對(duì)象放到F-Queue中,讓finalizer線程去執(zhí)行這些對(duì)象的finalize方法,如果這些對(duì)象沒有把自己“救活”,那么就會(huì)真正標(biāo)記為“死亡,可以回收”
finalize方法可靠嗎
finalize方法不可靠,優(yōu)先級(jí)極低,finalizer線程的優(yōu)先級(jí)也很低,虛擬機(jī)只保證會(huì)啟動(dòng)這個(gè)線程,但是不保證執(zhí)行結(jié)果,建議程序員忘掉這個(gè)方法。
保守式GC
什么是不明確的根
我們以調(diào)用棧為例,調(diào)用棧中存在函數(shù)的局部變量和參數(shù)的值,這些可以是引用也可以是int之類的基本數(shù)據(jù)類型。在棧中這些值就是一串?dāng)?shù)字的排列,所以無法知道哪些是指針哪些不是指針
指針和非指針的識(shí)別
- 檢查是不是正確對(duì)齊的值,在32位機(jī)器中,值應(yīng)該是4的倍數(shù),在64位中,值應(yīng)該是8的倍數(shù)
- 是不是指向堆內(nèi)
- 是不是指向?qū)ο蟮拈_頭
舉例說明貌似指針的非指針
例如有連個(gè)地址塊上的值都是0x00d0caf0,但是無法分辨到底是這到底是指針還是數(shù)值
貌似指針的非指針的對(duì)象會(huì)被保留
理解“不明確的數(shù)據(jù)結(jié)構(gòu)”
有的時(shí)候,堆里的對(duì)象中的數(shù)據(jù)也會(huì)存在無法分清是指針還是非指針的情況
相對(duì)應(yīng)的,就有“半保守式GC”,即在對(duì)象上帶有類信息,來確保數(shù)據(jù)結(jié)構(gòu)的明確
優(yōu)點(diǎn)
- 理解“語言處理程序不依賴于 GC”
缺點(diǎn)
- 理解“識(shí)別指針和非指針需要付出成本”
- 理解“錯(cuò)誤識(shí)別指針會(huì)壓迫堆”
- 理解“能夠使用的GC算法有限”
準(zhǔn)確式GC
HotSpot的算法實(shí)現(xiàn)
-
oopMap的作用是什么?為什么需要oopMap
為了能夠在GC時(shí)快速找到什么地方存著什么類型的對(duì)象,需要一個(gè)映射來存儲(chǔ)這些信息。
oopMap就用來保存偏移量和類型的映射 -
什么時(shí)候生成oopMap
在類加載完成之后,Hotspot會(huì)記錄對(duì)象內(nèi)的什么偏移量上是什么類型的數(shù)據(jù),然后保存在oopMap里
在JIT過程中,會(huì)在一些位置記錄下哪些偏移量是什么數(shù)據(jù) -
什么是安全點(diǎn)?為什么需要安全點(diǎn)?
在JIT過程中,不可以在每一條指令都記錄oopMap,這樣會(huì)浪費(fèi)很多的空間,所以需要找到一些不會(huì)導(dǎo)致引用關(guān)系變化的點(diǎn)來記錄,以便節(jié)約空間
-
通常那些地方可以作為安全點(diǎn)
方法調(diào)用、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等
-
通常在什么地方可以GC
在安全點(diǎn)的位置可以GC
-
GC時(shí)線程是立刻中斷嗎?
不會(huì)立即中斷,會(huì)執(zhí)行到安全點(diǎn)
-
線程中斷有哪幾種方式
一般有兩種方式,搶先式中斷和主動(dòng)式中斷。
搶先式中斷是先強(qiáng)制中斷所有的,然后如果沒有執(zhí)行到安全點(diǎn),就喚醒它讓它執(zhí)行到安全點(diǎn)。
主動(dòng)式中斷是設(shè)置一個(gè)標(biāo)志位,然后在安全點(diǎn)輪詢這個(gè)標(biāo)志,當(dāng)GC時(shí)把這個(gè)標(biāo)志設(shè)置為真,線程輪詢到標(biāo)志為真就中斷 -
什么是安全區(qū)域?為什么需要安全區(qū)域?
有的時(shí)候當(dāng)發(fā)生GC時(shí),線程是無法繼續(xù)執(zhí)行的,因此無法中斷,這時(shí)需要用安全區(qū)域解決。安全區(qū)域是指在一段代碼片段之中,引用關(guān)系都不會(huì)發(fā)生變化,在這個(gè)區(qū)域任何位置開始GC都是安全的。
簡(jiǎn)述Hotspot的分代GC策略
Hotspot將Java堆分成新生代和老年代,之前還有永久代。新生代中分為eden區(qū)和survivor區(qū),survivor區(qū)有兩種,分別是From區(qū)和To區(qū),默認(rèn)情況下,他們的大小比例為8:1:1。一般來說,新分配的對(duì)象放入eden區(qū),特別大的對(duì)象直接放入老年代。GC開始時(shí),eden區(qū)中存活的對(duì)象復(fù)制到To區(qū),F(xiàn)rom區(qū)中的對(duì)象如果年齡達(dá)到闕值就復(fù)制到老年代,否則復(fù)制到To區(qū),然后清空eden和From,同時(shí)將From和To調(diào)換,經(jīng)過一輪GC還存活的對(duì)象,將其年齡+1。如果To區(qū)不夠存放來自eden和From的存活對(duì)象時(shí),需要老年代做分配擔(dān)保,存在老年代中
Hotspot如何動(dòng)態(tài)判定對(duì)象年齡
如果一個(gè)survivor區(qū)中某個(gè)年齡對(duì)象的總和大于survivor區(qū)的一半,那么年齡大于等于這個(gè)年齡的直接進(jìn)入老年代
