相较于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容器环境
- 基准测试