運行時數據區域
線程私有
- 程序計數器 正在執行的字節碼指令的地址(native方法時爲undefined)
- Java虛擬機棧 存儲棧幀(局部變量表,操做數棧,動態連接,方法出口)OOM,StackOverflowError
- 本地方法棧 與虛擬機棧相似,是native方法的棧
線程共享
- Java堆 存放對象和數組 OOM
- 方法區(永久代)存儲已被虛擬機加載的類信息、常量、靜態變量 OOM
- 運行時常量池 方法區的一部分,編譯器生成的字面量和符號引用 OOM
直接內存
不是運行時數據區的一部分,可是也會產生OOM。算法
GC
垃圾檢測
- 引用計數 實現簡單且高效,但有循環引用的問題, 目前沒有虛擬機的實現採用這個算法
- 可達性分析 從GC Roots(虛擬機棧、方法區靜態屬性、方法區常量、本地方法棧)開始,沿引用鏈向下搜索,沒被引用鏈鏈接的對象就是無用對象
引用類型
- 強引用 代碼中廣泛存在的引用,只要強引用存在,被引用的對象就不會被回收
- 軟引用
SoftReference
,用來描述有用但並不是必須的引用,若是一次GC以後內存仍然不足,會把這些對象列入回收範圍進行第二次回收
- 弱引用
WeakReference
,下次GC一定會被回收
- 虛引用
PhantomReference
,不會對對象的生存時間產生影響,也沒法經過虛引用取得對象實例,用於在對象被回收時收到一個通知
方法區
判斷常量池中的對象是否須要回收與對象類似。 無用類的條件:數組
- 該類的全部實例都被回收
- 加載該類的類加載器已被回收
- 對應的Class對象沒在任何地方被引用
垃圾回收
- 標記-清除
- 標記須要回收的對象,再統一回收
- 效率不高,內存碎片
- 複製
- 將內存分爲兩塊,將存活的對象複製到to空間,將from空間統一清除
- 實現簡單,運行高效,內存利用率低,存活率較高時複製效率低
- 新生代分爲一個Eden和兩個Survivor,默認大小比爲8:1,每次只使用一個Survivor,GC時將新生代存活對象放到未使用的Survivor,放不下的晉升老年代(分配擔保)
- 標記-整理
分代收集
根據對象存活週期的不一樣,將內存劃分爲幾塊。新生代使用複製算法,老年代使用標記整理安全
Stop The World
可達性分析必須在一個能確保一致性的快照中進行,所以在GC進行時須要中止用戶線程。數據結構
安全點和安全區域
可達性分析的耗時較久(方法區就可能有幾百兆),所以使用了一組OopMap的數據結構,來存儲特定位置上對象內什麼偏離量上是什麼類型的數據,這樣在GC掃描時就能夠直接得到這些信息。多線程
可是會使OopMap內容變化的指令不少,若是每條指令都生成對應的OopMap,空間成本會很是高,所以只記錄了特定位置的信息,這些位置就稱爲安全點,線程只有到達安全點才能暫停。併發
具備方法調用、循環跳轉、異常跳轉功能的指令纔會產生安全點。spa
- 搶先式中斷(幾乎沒有虛擬機實現使用) GC發生時,中斷全部線程,若是有線程不在安全點,恢復線程,讓它跑到安全點
- 主動式中斷 不直接操做線程,而是在安全點檢查中斷標誌,判斷是否須要暫停
安全區域是安全點的擴展,引用關係不會變化的一段代碼片斷。線程進入安全區域時,標示本身進入了安全區域,要離開時則檢查根節點枚舉或整個GC過程是否完成,沒有則等待到收到能夠離開安全區的信號。線程
垃圾收集器
- Serial
- ParNew
- Serial的多線程版本,
- 垃圾回收時只有多個GC線程在工做
- Parallel Scavenge
- 新生代收集器,
- 並行的多線程收集器,
- 可控制的吞吐量(吞吐量優先)
- Serial Old
- Parallel Old
- CMS
- 老年代收集器,
- 使用標記清除算法,
- 追求最短回收停頓時間,
- 只在初始標記階段(只記錄GC Roots能關聯到的對象)和從新標記階段(修正併發標記時發生的引用變化,多線程)會STW,
- 對CPU資源敏感,併發時佔用必定資源,程序變慢,吞吐量變低
- 沒法清理浮動垃圾,須要預留必定空間在併發收集時供用戶線程使用,若是預留空間不夠,會臨時啓用Serial Old從新進行收集
- 內存碎片,找不到足夠大的連續內存時,提早觸發一次Full GC,能夠設置在要進行Full GC,進行一次隨便整理(或每幾回不壓縮的Full GC,整理一次碎片)。
- G1
- 新生代和老年代收集器,面向服務端應用的垃圾收集器
- 並行併發、分代收集、空間整合、可預測停頓
- 將堆分爲多個大小相等的獨立區域,新老生代再也不物理隔離,
- 有計劃的避免在整個堆作垃圾回收,優先回收價值最大的區域
- 追求低停頓可嘗試,追求吞吐量無優點
分配和回收策略
- 對象優先在Eden分配
- 大對象直接進入老年代 避免在Eden和Survivor中複製大量內存
- 長期存活對象進入老年代 每經歷一次GC,對象的年齡+1,達到必定歲數就晉升老年代
- 動態對象年齡判斷 Survivor中,相同年齡的全部對象大小總大於Survivor的一半,年齡大於等於該年齡的對象晉升老年代
- 空間分配擔保 Survivor空間不足,晉升老年代,有老年代空間不足風險;老年代連續空間小於新生代對象總大小或歷次晉升平均大小就會進行Full GC
其餘
finalize()
忘掉這個方法code
參考資料
《深刻理解Java虛擬機》第2版 《Java虛擬機規範》Java SE 8版對象