相較于CMS,G1還不具備全方位的壓倒性優勢,比如在用戶線程運行過程中,G1無論是為了垃圾收集產生的內存占用,還是程序運行時的額外執行負載都要比CMS要高。**通常情況下,在小內存的應用上CMS的表現大概率上會優于G1,G1在大內存應用上會發揮其最大的效率,兩者的性能平衡點在6-8g內存之間**
主要區別:
1) 堆(Heap)空間分配不同
* CMS 將堆邏輯上分成Eden,Survivor(S0,S1),Old,并且他們是固定大小JVM啟動的時候就已經設定不能改變,并且是連續的內存塊
* G1 將堆分成多個大小相同的Region(區域),默認2048個,在1Mb到32Mb之間大小,邏輯上分成Eden,Survivor,Old,巨型,空閑,他們不是固定大小,會根據每次GC的信息做出調整
2) 并發標記階段三色標記算法處理結果不同
* CMS 在三色標記算法階段,如果將白色對象重新分配給黑色對象時,在分配期間采用增量更新方式(寫屏障中發現白色對象引用被分配給黑色對象時,分配過程中將白色重新設置為灰色,即插入的時候就記錄修改)
* G1 在三色標記算法階段,如果將白色對象重新分配給黑色對象時,采用SATB,并發標記階段,所有被改變的對象入隊,在寫屏障中統一處理為灰色
3) 壓縮策略不同
* CMS中不啟用壓縮會產生很多內存碎片,當產生很多內存碎片的時候,找不到空間來分配剩余的對象,或者設定參數,使它合并相鄰的的空閑內存,當合并超過一定次數后觸發Full GC,進行壓縮
* G1中每次回收過程中,將多個Region拷貝到空閑Region的時候都會進行壓縮
4) 可預測停頓:相比CMS,G1可以設定每次GC的時間,從而讓GC在規定時間內回收效益最大的內存
5) GC策略不同
* CMS中,GC的策略分為Young GC,Old GC,Full GC
* G1中,GC的策略分為Young GC,Mixed GC,Full GC
6.Young GC不同
CMS的Young GC就是依賴并行GC(ParNew)去完成的.只有老年代中使用CMS GC(也就是Old GC)
CMS 使用分代回收,堆被分成了年輕代和老年代,其中年輕代回收依賴ParNew去回收,需要STW
* a.年輕代分成了Eden,Survivor(S0,S1),當Eden區域滿了就觸發Young GC,將Eden中存活的數據復制到S0或者S1中的一個中去,如果對象太大,Survivor中無法分配就直接存到Old去,然后回收垃圾的數據
* b.這時Eden被清空了,Survivor其中一個被填了數據
* c.等到Eden再次滿了,就會再次存入之前的Survivor中去,如果Survivor也滿了,就會將將Eden和Survivor中存活的對象復制到另外一個Survivor區域中,每次GC一次依然存活的對象的age都+1,默認當age=15的時候(經歷了多次Young GC)那么就會晉升到Old去,然后清空Eden和Survivor
G1的Young GC是自己去清理的,而不是并行GC處理
* a.Eden區域數據滿了
* b.將Eden區域中存活的復制到Survivor中去,其中符合條件的晉升,如果Survivor空間不夠,那么將Survivor和Eden中存活的數據一起復制到一個新的區域中去,這時這個新的區域就是Survivor,然后回收掉之前的Eden和Survivor
* c.每次Young GC之后,根據之前一次GC的信息調整Eden和Survivor的大小,因為G1中這兩個區域并不是物理上連續的內存,而只是邏輯上的,因此可以動態分配
- 前言
- Write once, run anywhere
- 概述
- JAVA虛擬機
- JVM整體結構
- JVM架構模型
- JVM虛擬機分類
- HotSpot VM
- JRockit
- IBM-J9
- Azul/zing VM
- Taobao VM
- Dalvik VM
- Graal VM
- JAVA源碼編譯機制
- Javac編譯器
- 分析和輸入到符號表
- 注解處理
- 語義分析和生成class文件
- ECJ編譯器
- 類執行機制
- 字節碼解釋執行
- 棧頂緩存
- 部分棧幀共享
- 編譯執行
- 即時編譯器
- C1 Compiler
- C2 Compiler
- Graal編譯器
- C1與C2編譯器
- AOT
- 編譯優化
- 字符串優化
- 方法內聯
- 逃逸分析
- 同步消除
- 標量替換
- 棧上分配
- 去虛擬化/逆優化
- 多層編譯
- JVM編譯策略
- OSR編譯
- 冗余削除
- CodeCache
- 常量編譯優化
- JVM運行時數據區
- 程序計數器
- JAVA虛擬機棧
- 棧幀
- 局部變量表
- 操作數棧
- 本地方法棧
- Java調用native方法
- JVM Stacks && Native Stacks
- 堆-Heap
- 方法區(Method Area)
- 運行時常量池
- 常量傳播優化
- MetaSpace
- 直接內存
- StackOverflowError
- 遞歸方法
- OutOfMemoryError
- 本地內存溢出
- 執行引擎
- 運行時數據區關聯關系
- jdk8內存結構
- JMM內存模型
- JAVA內存模型
- JMM八種操作指令
- 內存屏障
- 指令重排
- as-if-serial語義
- Happen-Before規則
- 數據依賴性
- 原子性、可見性與有序性
- 偽共享
- CPU三級緩存
- 緩存行
- MESI協議
- Java中的偽共享
- ConcurrentHashMap偽共享解決方案
- 虛擬機對象
- 對象創建原理
- 對象內存布局
- 對象頭
- 實例數據
- 對象的訪問定位
- 垃圾收集器與內存分配策略
- GC相關概念
- TLAB
- JVM GC工作原理
- 內存管理
- JAVA引用分類
- 死亡標記
- 回收方法區
- 三色標記算法
- 垃圾收集算法
- 標記-清除算法
- 標記-整理算法
- 復制算法
- 分代收集算法
- HotSpot算法實現
- STW
- 垃圾收集器
- 常見的垃圾收集器
- 垃圾收集器分類
- Serial收集器
- Serial Old收集器
- ParNew收集器
- Parallel Scavenge收集器
- Parallel Old收集器
- CMS收集器
- CMS完整收集過程
- Card Table
- G1收集器
- 分代收集
- 空間整合
- 可預測的停頓時間模型
- G1&CMS
- 主要參數說明
- G1適用場景
- Remembered Set
- G1垃圾回收的過程
- G1優化建議
- Shenandoah
- ZGC
- 垃圾收集器特點
- GC日志
- GC策略的評價指標
- jvm card table數據結構
- 對象生存軌跡
- 類文件結構
- 魔數
- 版本號
- 常量池
- 訪問標志
- 父類索引
- 接口集合
- 字段集合
- 方法集合
- 屬性集合
- 類加載機制與類的初始化
- Java代碼執行流程
- 類加載過程
- 抽象類ClassLoader
- 常見類加載器
- BootstrapClassLoader
- 自定義類加載器
- 線程上下文類加載器
- 雙親委派模型
- Tomcat類加載機制
- ServiceLoader
- 類的初始化
- 常見的JVM類加載異常
- ClassNotFoundException
- NoClassDefFoundError
- LinkageError
- ClassCastException
- 虛擬機性能調優監控與故障處理工具
- CPU利用率高/飆升
- 排查及解決方案
- 上下文切換
- GC問題定位解決方案
- prommotion failed
- FullGC頻繁
- youngGC
- 內存問題
- 內存溢出和內存泄漏
- 內存溢出
- 棧溢出
- 堆溢出
- 對外內存溢出
- 內存泄漏
- 磁盤問題
- 線上問題解決方案
- 不定期出現的接口耗時現象
- 線程池異常
- 死鎖問題
- JVM調優
- jvm參考配置
- jvm-jstat
- jvm-jmap
- jvm-jstack
- jinfo
- jps
- 虛擬機的退出
- Shutdown Hook
- JVM指令
- 附錄
- 常用JVM指令
- Class文件版本號
- Class文件格式
- 方法訪問標識
- jvm常量池
- 類或接口的訪問標識
- 描述符標識字符含義
- 字段訪問標識
- Java程序與Docker容器環境
- 基準測試