運行時數(shù)據(jù)區(qū)域
程序計數(shù)器
當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器,雇锡,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條要執(zhí)行的字節(jié)碼,每個線程有獨立的程序計數(shù)器曙痘,是線程私有的
Java虛擬機棧
也是線程私有的立肘,生命周期與線程相同谅年。虛擬機棧是描述Java方法執(zhí)行的內(nèi)存模型,每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀(stack frame)用于存放局部變量表融蹂、操作棧、動態(tài)鏈接和方法出口等信息区拳。每一個方法從開始執(zhí)行到結(jié)束就對應(yīng)著一個棧幀在虛擬機棧從入棧到出棧的過程樱调。
- 線程請求棧深度大于虛擬機允許的深度,拋出StackOverflowError的異常
- 當(dāng)虛擬機動態(tài)擴展后仍無法申請足夠的內(nèi)存時拋出OutOfMemoryError的異常
Java方法棧
與Java虛擬機棧類似圣猎,為虛擬機使用到的native方法進行服務(wù)
Java堆
堆是被所有線程共享的內(nèi)存區(qū)域菩颖,用于存放對象實例,是垃圾收集器管理的主要區(qū)域,不一定在連續(xù)的內(nèi)存空間上鳍怨,只要邏輯連續(xù)即可鞋喇。
方法區(qū)
是各個線程共享的區(qū)域,用于存放已被虛擬機加載的類信息落塑、常量罐韩、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)。
運行時常量池
是方法區(qū)的一部分龙考,用于存放編譯期間生成的各種字面量和符號引用矾睦。
垃圾收集器和內(nèi)存分配策略
根搜索算法
通過GC root的對象作為起點,從這些節(jié)點開始向下搜索缓溅,搜索的過程稱為引用鏈赁温,當(dāng)一個對象到GC root沒有任何引用鏈相連的時候,則證明此對象是不可用的酝陈〕涟铮可以作為GC root的對象包括以下幾種
- 虛擬機棧中引用的對象
- 方法區(qū)中的類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- native方法中引用的對象
引用
- 強引用(String Reference): 只要強引用還在,垃圾收集器永遠不會回收被引用對象
- 軟引用(Soft Reference): 在內(nèi)存不足時待牵,垃圾收集器會回收這部分對象
- 弱引用(Weak Reference): 弱引用的對象只能存活到下次GC之前喇勋,無論內(nèi)存是否夠用都會被回收
- 虛引用(Phantom Reference): 一個對象是否有虛引用都不會對它的生存時間產(chǎn)生影響,也無法通過虛引用來取得一個對象實例贰拿,熄云,為對象設(shè)置虛引用的目的是希望這個對象被回收時收到一個系統(tǒng)通知
對象回收過程
一個對象被回收需要經(jīng)歷兩次標記過程缴允,如果一個對象在經(jīng)過根搜索之后沒有與GC root相連的引用鏈,那么它會被第一次標記并進行篩選矗漾,篩選的條件是該對象有沒有必要執(zhí)行finalize()方法薄料,沒有必要執(zhí)行的條件
- 對象有沒有覆蓋finalize()
- finalize()是否已經(jīng)被虛擬機調(diào)用過
如果被認為有必要執(zhí)行,這個對象會被放到F-Queue的隊列中嫡锌,并放在由一條虛擬機創(chuàng)建的低優(yōu)先級Finalizer線程中去執(zhí)行琳钉。執(zhí)行操作由虛擬機觸發(fā),不保證會等到執(zhí)行結(jié)束啦桌。稍后GC將對F-Queue中的對象進行二次小規(guī)模標記,如果對象成功在finalize()方法中重新和引用鏈上任意對象創(chuàng)建關(guān)聯(lián)且改,在二次標記的時候會被移除F-Queue板驳,如果沒有的話就會很快被回收。
- finalize()只會被調(diào)用一次
回收方法區(qū)
收益比較低慨蓝,主要回收兩部分內(nèi)容
- 廢棄常量
- 無用的類
如果常量池中有常量不再被引用礼烈,將被從常量池中移除婆跑。判定無用的類的條件
- 該類所有實例都應(yīng)經(jīng)被回收,即Java堆中不存在該類的任何實例
- 加載該類的ClassLoader被回收
- 該類對應(yīng)的java.lang.Class對象沒有被引用犀忱,無法通過反射訪問該類
垃圾收集算法
標記-清除算法
首先標記需要回收的對象郊供,在標記完后統(tǒng)一進行回收,缺點
- 效率問題: 標記和清除的效率都不高
- 空間問題: 會產(chǎn)生大量不連續(xù)的內(nèi)存碎片
復(fù)制算法
劃分兩塊大小相等的內(nèi)存,每次用一塊疯淫,當(dāng)這塊內(nèi)存用完了戳玫,就將活著的內(nèi)存復(fù)制到另一塊內(nèi)存上咕宿,然后把已使用的內(nèi)存清理掉。
-
優(yōu)點
- 實現(xiàn)簡單
- 運行高效
-
缺點
- 內(nèi)存使用量翻倍
用來回收新生代缆镣,將內(nèi)存分為較大的Eden區(qū)和兩塊較小的Survivor區(qū)试浙,當(dāng)回收時將Eden和Survivor中存活的對象拷貝到另一個Survivor中,然后清除掉Eden和第一塊Survivor钠糊,當(dāng)Survivor空間不足時這些對象會被存放到老年代。HotSpot虛擬機默認Eden和Survivor的大小比例是8:1艘刚。
標記-整理算法
老年代中使用截珍,過程和標記-清除算法一樣,后續(xù)不回收云稚,而是將對象向一端移動沈堡,最后直接清除掉邊界以外的內(nèi)存
分代收集算法
新生代--復(fù)制算法
老年代--標記整理算法
垃圾收集器
Serial收集器
單線程收集器诞丽,進行垃圾收集時必須暫停其它所有工作線程
ParNew收集器
多線程的Serial收集器
Parallel Scavenge收集器
使用復(fù)制算法的收集器僧免,和其它收集器區(qū)別
-
CMS等關(guān)注用戶線程等待時間,Parallel Scavenge收集器的目的是達到一個可控制的吞吐量撞叨,高吞吐量意味著更高效率的使用CPU浊洞,主要用于后臺不需要交互的任務(wù)
吞吐量=運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)
Serial Old收集器
Serial的老年代版本,使用標記-整理算法
Parallel Old收集器
Parallel Scavenge的老年代版本枷餐,使用標記整理算法
CMS(Concurrent Mark Sweep)收集器
CMS時一種獲取最短停頓時間為目標的收集器
-
過程
- 初始標記
- 并發(fā)標記
- 重新標記
- 并發(fā)清除
-
缺點
- 對CPU資源敏感
- 無法處理浮動垃圾毛肋,可能出現(xiàn)Concurrent Mode Failure失敗導(dǎo)致Full GC
- CMS采用標記-清除算法屋剑,會產(chǎn)生內(nèi)存碎片
G1收集器
- 基于標記-整理算法
- 可以非常精準的控制停頓,可以不犧牲吞吐量的前提下完成低停頓的內(nèi)存回收
內(nèi)存分配與回收策略
- 對象優(yōu)先在Eden分配: 當(dāng)Eden沒有足夠的空間時虛擬機發(fā)起一次Minor GC
- 大對象直接進入老年代: 大對象指需要大量連續(xù)儲存空間的Java對象趁桃,例如長字符串卫病、數(shù)組(byte[])等
- 長期存活對象進入老年代: 虛擬機給每個對象定義一個對象年齡計數(shù)器,對象在Eden生成后進過一次Minor GC后仍能存活益咬,并能放入Survivor中帜平,年齡設(shè)為1,以后每經(jīng)過一次Minor GC年齡都加1冗锁,當(dāng)年齡增加到一定閾值的時候進入老年代
- 動態(tài)對象年齡判定: 如果在Survivor中相同年齡所有對象大小的綜合等于Survivor一半的時候嗤栓,年齡大于等于該年齡進入老年代
參考
- 深入理解Java虛擬機, 周志明