ElasticSearch 垃圾回收器优化

背景介绍

  • es版本:6.3.2
  • 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表示survivor区域允许容纳的最大空间大小为56688640 bytes
  • max 6 表示对象经过6次gc后依然存活直接进入老年代
  • 对象列表为此次gc之后,survivor当前存活对象的年龄大小分布,下次gc如果对象没释放的话,超过阈值的(age=6 or 占用空间 > 56688640)对象将晋升到old generation。

问题解决


通过指标数据,发现es集群存在新生代内存分配过小,导致young gc 频繁。 通过命令查看
jstat -gc pid 1000 1000
发现新生代内存仅分配了约1g,而老年代占到了29g。一种解决方案就是增大新生代,具体大小需要根据经验和调整后指标数据决定。
此外,调研发现,一些大的互联网公司,如美团,携程,es的垃圾回收器使用的都是G1。综上,直接替换es的垃圾回收器为G1。


直接替换为


效果对比图
未升级的机器:


升级为G1的机器:

  • 升级前young gc平均耗时约为15ms,升级后约为1ms
  • 升级前young gc比较频繁,升级后young gc 次数明显减少
  • 升级前cpu偶尔有毛刺现象,升级后cpu整体比较稳定

对ES使用G1以后,Young GC的频率和耗时都可以极大的降低,Old GC几乎不会出现。

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工作机制


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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值