JMM内存模型
堆的结构(jdk8)
分代:
-
年轻代(Eden区,Survivor区)
年轻代存储新创建的对象,里面的对象朝生夕死,分为一个Eden区和两个survivor区,便于使用标记复制算法,提高回收效率,减少内存碎片
-
老年代
存储大对象及年轻代多次回收下逃生的对象,这些对象长期有用,数量大,使用复制算法不太合适,使用标记清除操作少部分对象,存在内存碎片
GC垃圾回收
两种GC:
- YGC:回收年轻代对象
- FGC:回收年轻代+老年代+元数据区
为什么要分代回收?
因为对象存活的周期不同,有的对象会长久存在,有的对象朝生夕死,将它们区分开,使回收更加细粒度化,可缩短回收时间
堆的结构(jdk11)
逻辑分代(不存在实际的物理区域划分):
分区:每一个分区既可能是年轻代,也可能是老年代,但同一时刻只能是一种
- 年轻代:标记复制算法
- 老年代:标记整理算法
- 大对象区:G1执行器新增了大对象区,专门存储大对象
三种GC:
- YGC:回收年轻代对象
- MGC:回收年轻代+老年代+元数据区(可部分回收老年代,追求性价比)
- FGC:串行,会暂停应用线程,应极力避免
算法
2种基础回收算法
-
引用计数法:存在循环引用问题,基本没人用
-
根搜索算法:当前主流的垃圾回收基础算法
3种回收算法实现
基础思想:操作的对象越少,回收的效率越高
- 标记复制算法:Parnew执行器回收年轻代,年轻代存活对象少,复制存活对象成本低
- 标记清除算法:CMS执行器回收老年代,老年代存活对象多,标记存活对象,回收未被标记的对象
- 标记整理算法:G1执行器,YGC和MGC采用复制算法,FGC采用标记整理算法,标记清除算法的升级,将所有存活对象压缩到连续地址上
标记算法
三色标记算法:垃圾回收前要进行标记,3色标记支持异步执行,可以不中断应用线程进行标记
标记次数: 每个对象被标记两次。
标记过程: 最开始,除GC ROOTS所有对象都是白色,初始标记,引用对象 A 标为灰色,并发标记扫描 A 下 的对象 B,C将 B,C标为灰色,A标记为黑色,之后开始重复标记。
缺点:
1、多标产生浮动垃圾。原存在黑色A引用黑色叶子B, 随后引用关系断开,至此B应该被回收,但是黑色不会回收就产生了浮动垃圾,还有新对象(直接标黑色),但是浮动垃圾不回收不会有很大影响,下次回收即可。
2、漏标造成对象错误回收。比如黑色对象A引用了原白色对象B,A是黑色不会被扫描,所以B会造成漏标,导致B被回收,回收正在使用的对象直接导致程序异常。
参数调优
优化jdk1.8版本参数
升级G1执行器,配置全套参数
CMS调优:
官方建议:
1、年轻代大小为堆的3/8左右
2、堆大小=年轻代大小+年老代大小,即xmx=xmn+老年代大小
3、年轻代xmn设置为老年代存活对象的1~1.5倍
4、老年代大小设置为老年代存活对象的2~3倍
G1调优:
1. 不要设置新生代,老年代大小,使自动调节,以达到预期的暂停时间
2. 不断调优暂停时间,可以先设一个理想的比较小的值,观察GC频率,FULL GC频次,逐渐放大到一个比较合理的暂停时间
3. 使用-XX:ConcGCThreads=n来增加标记线程的数量,缩短并发周期
4. 分区大小,公式如下
(Xms + Xmx ) /2 / 2048 , 不大于32M,不小于1M,且为2的指数
7. 决定区域回收的存活对象的比例
8. 适当增加堆内存
9. Xms和Xmx设置大小一致