運(yùn)行時(shí)數(shù)據(jù)區(qū)域
程序計(jì)數(shù)器
當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條要執(zhí)行的字節(jié)碼,每個(gè)線程有獨(dú)立的程序計(jì)數(shù)器,是線程私有的
Java虛擬機(jī)棧
也是線程私有的,生命周期與線程相同。虛擬機(jī)棧是描述Java方法執(zhí)行的內(nèi)存模型,每個(gè)方法被執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀(stack frame)用于存放局部變量表、操作棧、動(dòng)態(tài)鏈接和方法出口等信息。每一個(gè)方法從開(kāi)始執(zhí)行到結(jié)束就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧從入棧到出棧的過(guò)程。
- 線程請(qǐng)求棧深度大于虛擬機(jī)允許的深度,拋出StackOverflowError的異常
- 當(dāng)虛擬機(jī)動(dòng)態(tài)擴(kuò)展后仍無(wú)法申請(qǐng)足夠的內(nèi)存時(shí)拋出OutOfMemoryError的異常
Java方法棧
與Java虛擬機(jī)棧類(lèi)似,為虛擬機(jī)使用到的native方法進(jìn)行服務(wù)
Java堆
堆是被所有線程共享的內(nèi)存區(qū)域,用于存放對(duì)象實(shí)例,是垃圾收集器管理的主要區(qū)域,不一定在連續(xù)的內(nèi)存空間上,只要邏輯連續(xù)即可。
方法區(qū)
是各個(gè)線程共享的區(qū)域,用于存放已被虛擬機(jī)加載的類(lèi)信息、常量、靜態(tài)變量和即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
運(yùn)行時(shí)常量池
是方法區(qū)的一部分,用于存放編譯期間生成的各種字面量和符號(hào)引用。
垃圾收集器和內(nèi)存分配策略
根搜索算法
通過(guò)GC root的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索的過(guò)程稱(chēng)為引用鏈,當(dāng)一個(gè)對(duì)象到GC root沒(méi)有任何引用鏈相連的時(shí)候,則證明此對(duì)象是不可用的。可以作為GC root的對(duì)象包括以下幾種
- 虛擬機(jī)棧中引用的對(duì)象
- 方法區(qū)中的類(lèi)靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
- native方法中引用的對(duì)象
引用
- 強(qiáng)引用(String Reference): 只要強(qiáng)引用還在,垃圾收集器永遠(yuǎn)不會(huì)回收被引用對(duì)象
- 軟引用(Soft Reference): 在內(nèi)存不足時(shí),垃圾收集器會(huì)回收這部分對(duì)象
- 弱引用(Weak Reference): 弱引用的對(duì)象只能存活到下次GC之前,無(wú)論內(nèi)存是否夠用都會(huì)被回收
- 虛引用(Phantom Reference): 一個(gè)對(duì)象是否有虛引用都不會(huì)對(duì)它的生存時(shí)間產(chǎn)生影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例,,為對(duì)象設(shè)置虛引用的目的是希望這個(gè)對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知
對(duì)象回收過(guò)程
一個(gè)對(duì)象被回收需要經(jīng)歷兩次標(biāo)記過(guò)程,如果一個(gè)對(duì)象在經(jīng)過(guò)根搜索之后沒(méi)有與GC root相連的引用鏈,那么它會(huì)被第一次標(biāo)記并進(jìn)行篩選,篩選的條件是該對(duì)象有沒(méi)有必要執(zhí)行finalize()方法,沒(méi)有必要執(zhí)行的條件
- 對(duì)象有沒(méi)有覆蓋finalize()
- finalize()是否已經(jīng)被虛擬機(jī)調(diào)用過(guò)
如果被認(rèn)為有必要執(zhí)行,這個(gè)對(duì)象會(huì)被放到F-Queue的隊(duì)列中,并放在由一條虛擬機(jī)創(chuàng)建的低優(yōu)先級(jí)Finalizer線程中去執(zhí)行。執(zhí)行操作由虛擬機(jī)觸發(fā),不保證會(huì)等到執(zhí)行結(jié)束。稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行二次小規(guī)模標(biāo)記,如果對(duì)象成功在finalize()方法中重新和引用鏈上任意對(duì)象創(chuàng)建關(guān)聯(lián),在二次標(biāo)記的時(shí)候會(huì)被移除F-Queue,如果沒(méi)有的話(huà)就會(huì)很快被回收。
- finalize()只會(huì)被調(diào)用一次
回收方法區(qū)
收益比較低,主要回收兩部分內(nèi)容
- 廢棄常量
- 無(wú)用的類(lèi)
如果常量池中有常量不再被引用,將被從常量池中移除。判定無(wú)用的類(lèi)的條件
- 該類(lèi)所有實(shí)例都應(yīng)經(jīng)被回收,即Java堆中不存在該類(lèi)的任何實(shí)例
- 加載該類(lèi)的ClassLoader被回收
- 該類(lèi)對(duì)應(yīng)的java.lang.Class對(duì)象沒(méi)有被引用,無(wú)法通過(guò)反射訪問(wèn)該類(lèi)
垃圾收集算法
標(biāo)記-清除算法
首先標(biāo)記需要回收的對(duì)象,在標(biāo)記完后統(tǒng)一進(jìn)行回收,缺點(diǎn)
- 效率問(wèn)題: 標(biāo)記和清除的效率都不高
- 空間問(wèn)題: 會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片
復(fù)制算法
劃分兩塊大小相等的內(nèi)存,每次用一塊,當(dāng)這塊內(nèi)存用完了,就將活著的內(nèi)存復(fù)制到另一塊內(nèi)存上,然后把已使用的內(nèi)存清理掉。
-
優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單
- 運(yùn)行高效
-
缺點(diǎn)
- 內(nèi)存使用量翻倍
用來(lái)回收新生代,將內(nèi)存分為較大的Eden區(qū)和兩塊較小的Survivor區(qū),當(dāng)回收時(shí)將Eden和Survivor中存活的對(duì)象拷貝到另一個(gè)Survivor中,然后清除掉Eden和第一塊Survivor,當(dāng)Survivor空間不足時(shí)這些對(duì)象會(huì)被存放到老年代。HotSpot虛擬機(jī)默認(rèn)Eden和Survivor的大小比例是8:1。
標(biāo)記-整理算法
老年代中使用,過(guò)程和標(biāo)記-清除算法一樣,后續(xù)不回收,而是將對(duì)象向一端移動(dòng),最后直接清除掉邊界以外的內(nèi)存
分代收集算法
新生代--復(fù)制算法
老年代--標(biāo)記整理算法
垃圾收集器
Serial收集器
單線程收集器,進(jìn)行垃圾收集時(shí)必須暫停其它所有工作線程
ParNew收集器
多線程的Serial收集器
Parallel Scavenge收集器
使用復(fù)制算法的收集器,和其它收集器區(qū)別
-
CMS等關(guān)注用戶(hù)線程等待時(shí)間,Parallel Scavenge收集器的目的是達(dá)到一個(gè)可控制的吞吐量,高吞吐量意味著更高效率的使用CPU,主要用于后臺(tái)不需要交互的任務(wù)
吞吐量=運(yùn)行用戶(hù)代碼時(shí)間 / (運(yùn)行用戶(hù)代碼時(shí)間 + 垃圾收集時(shí)間)
Serial Old收集器
Serial的老年代版本,使用標(biāo)記-整理算法
Parallel Old收集器
Parallel Scavenge的老年代版本,使用標(biāo)記整理算法
CMS(Concurrent Mark Sweep)收集器
CMS時(shí)一種獲取最短停頓時(shí)間為目標(biāo)的收集器
-
過(guò)程
- 初始標(biāo)記
- 并發(fā)標(biāo)記
- 重新標(biāo)記
- 并發(fā)清除
-
缺點(diǎn)
- 對(duì)CPU資源敏感
- 無(wú)法處理浮動(dòng)垃圾,可能出現(xiàn)Concurrent Mode Failure失敗導(dǎo)致Full GC
- CMS采用標(biāo)記-清除算法,會(huì)產(chǎn)生內(nèi)存碎片
G1收集器
- 基于標(biāo)記-整理算法
- 可以非常精準(zhǔn)的控制停頓,可以不犧牲吞吐量的前提下完成低停頓的內(nèi)存回收
內(nèi)存分配與回收策略
- 對(duì)象優(yōu)先在Eden分配: 當(dāng)Eden沒(méi)有足夠的空間時(shí)虛擬機(jī)發(fā)起一次Minor GC
- 大對(duì)象直接進(jìn)入老年代: 大對(duì)象指需要大量連續(xù)儲(chǔ)存空間的Java對(duì)象,例如長(zhǎng)字符串、數(shù)組(byte[])等
- 長(zhǎng)期存活對(duì)象進(jìn)入老年代: 虛擬機(jī)給每個(gè)對(duì)象定義一個(gè)對(duì)象年齡計(jì)數(shù)器,對(duì)象在Eden生成后進(jìn)過(guò)一次Minor GC后仍能存活,并能放入Survivor中,年齡設(shè)為1,以后每經(jīng)過(guò)一次Minor GC年齡都加1,當(dāng)年齡增加到一定閾值的時(shí)候進(jìn)入老年代
- 動(dòng)態(tài)對(duì)象年齡判定: 如果在Survivor中相同年齡所有對(duì)象大小的綜合等于Survivor一半的時(shí)候,年齡大于等于該年齡進(jìn)入老年代
參考
- 深入理解Java虛擬機(jī), 周志明