- es集群配置:16核cpu,内存64G,磁盘200G
- JDK版本:1.8
- 垃圾回收器: CMS+ParNew
部署在这个集群的服务偶尔会遇到服务超时的情况,从kibana监控中可以看到,服务超时情况发生时,es服务器cpu较高。es存在young gc频繁,old gc 低频率,每天约出现2-4次。
查看过去一小时的监控情况,发现young gc 比较频繁,大量对象最终进入了老年代,通过old gc被回收掉了。
查看GC日志,log里99%都是GC (Allocation Failure)造成的young gc。Allocation Failure表示向young generation(eden)给新对象申请空间,但是young generation(eden)剩余的合适空间不够所需的大小导致的minor gc。
Desired survivor size 56688640 bytes, new threshold 6 (max 6)
- age 1: 6717288 bytes, 6717288 total
- age 2: 6025032 bytes, 12742320 total
- age 3: 987872 bytes, 13730192 total
- age 4: 176 bytes, 13730368 total
- age 5: 336 bytes, 13730704 total
- age 6: 93864 bytes, 13824568 total
- Desired survivor size表示survivor区域允许容纳的最大空间大小为56688640 bytes
- max 6 表示对象经过6次gc后依然存活直接进入老年代
- 对象列表为此次gc之后,survivor当前存活对象的年龄大小分布,下次gc如果对象没释放的话,超过阈值的(age=6 or 占用空间 > 56688640)对象将晋升到old generation。
JVM 垃圾回收
当代主流虚拟机(Hotspot VM)的垃圾回收都采用“分代回收”的算法。“分代回收”是基于这样一个事实:对象的生命周期不同,所以针对不同生命周期的对象可以采取不同的回收方式,以便提高回收效率。
- 新生代:分三个区:一个Eden区,两个Survivor区,默认内存占比8:1:1。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到两个Survivor区(中的一个)。当这个Survivor区满时,此区的存活且不满足“晋升”条件的对象将被复制到另外一个Survivor区。对象每经历一次Minor GC,年龄加1,达到“晋升年龄阈值”后,被放到老年代。
- 老年代:在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代。
- java8已经没有持久代了,改为元数据区,主要存放元数据,例如Class、Method的元信息。
对象分配过程
- 对象比较大的时候,超过-XX:PretenureSizeThreshold设置值时,直接分配到老年代;
- 向eden申请空间创建新对象,eden没有合适的空间,因此触发minor gc
- minor gc将eden区及from survivor区域的存活对象进行处理:
- 如果这些对象年龄达到阈值(MaxTenuringThreshold),则直接晋升到年老代
- 若要拷贝的对象太大,那么不会拷贝到to survivor,而是直接进入年老代
- 若to survivor区域空间不够/或者复制过程中出现不够,则发生survivor溢出,直接进入年老代
- 其他的,若to survivor区域空间够,则存活对象拷贝到to survivor区域
- 此时eden区及from survivor区域的剩余对象为垃圾对象,直接抹掉回收,释放的空间成为新的可分配的空间
- minor gc之后,若eden空间足够,则新对象在eden分配空间;若eden空间仍然不够,则新对象直接在年老代分配空间
垃圾回收器
- 新生代收集器有:Serial(单线程),ParNew(多线程),Paraller Scavenge(侧重于吞吐量控制)
- 老年代收集器有:CMS(获取最短回收停顿时间为目标的回收器,该回收器是基于“标记-清除”算法实现的), Serial old,Parallel Old
- G1收集器可作用与新生代和老年代(JDK9默认垃圾收集器)
ParNew+CMS工作机制
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75 //老年代内存使用率超过75%触发垃圾回收
-XX:+UseCMSInitiatingOccupancyOnly
ParNew:复制算法,将内存分为大小相等的两块,每次使用其中的一块一块用完时,将存活的对象复制到另一块。 CMS:使用标记-清除算法。整个过程分为四步:
- 初始标记:STW,标记GC Roots能关联到的对象,速度很快
- 并发标记:GC Roots Tracing过程。耗时。和用户线程一起执行(并行)
- 重新标记:STW,标记并发标记过程中程序运行导致标记变化的对象,时间比初始标记长,远比并发标记短
- 并发清除:耗时。和用户线程一起执行(并行)
G1
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色:
- G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。
- G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。并基于用户指定的停顿时间来选择进行垃圾回收的区块数量。G1 采用增量回收的方式,每次回收一些区块,而不是整堆回收。
- G1 收集线程在标记阶段和应用程序线程并发执行,标记结束后,G1 也就知道哪些区块基本上是垃圾,存活对象极少,G1 会先从这些区块下手,因为从这些区块能很快释放得到很大的可用空间,这也是为什么 G1 被取名为 Garbage-First 的原因。
G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。如下图所示:
Remembered Sets(Rset)
逻辑上说每个Region都有一个RSet,RSet记录了其他Region中的对象引用本Region中对象的关系。
Collection Set(CSet)
记录了GC要收集的Region集合,集合里的Region可以是任意年代的。
G1工作模式
- YoungGC年轻代收集
在分配一般对象(非巨型对象)时,当所有eden region使用达到最大阀值并且无法申请足够内存时,会触发一次YoungGC。每次younggc会回收所有Eden以及Survivor区,并且将存活对象复制到Old区以及另一部分的Survivor区。
- mixed gc
最后
由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦
-8GIZV0hh-1628146035376)]
[外链图片转存中…(img-V5Pdpaf4-1628146035377)]
[外链图片转存中…(img-Ijc04DLI-1628146035378)]