elasticsearch出现OutOfMemoryError故障分析及处理

最近我们的elasticsearch6.6.1集群出现了停止服务的故障,客户端提示多个节点连接无响应。检查服务器上的日志,发现如下错误日志:

在这里插入图片描述

从错误日志可以看到,jvm首先进行了Full GC。一般老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象。所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。

如果执行Full GC后空间仍然不足,则会抛出如下错误:

java.lang.OutOfMemoryError: Java heap space

思路

当然,我们首先想到的肯定是调整该节点的jvm启动参数,比如提高jvm.options的xmx等参数,然后重启集群恢复服务。

但是这只是治标不治本的方法,我在参考[2]中找到了elasticsearch出现这个问题的原因和解决办法:

因为默认情况下es对字段数据缓存(Field Data Cache)大小是无限制的,查询时会把字段值放到内存,特别是facet查询,对内存要求非常高,它会把结果都放在内存,然后进行排序等操作,一直使用内存,直到内存用完,当内存不够用时就有可能出现out of memory错误。

解决方法

  1. 设置es的缓存类型为Soft Reference,它的主要特点是据有较强的引用功能。只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引 用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。在es的配置文件加上index.cache.field.type: soft即可。

  2. 设置es最大缓存数据条数和缓存失效时间,通过设置index.cache.field.max_size: 50000来把缓存field的最大值设置为50000,设置index.cache.field.expire: 10m把过期时间设置成10分钟。

另外补充一下,通过优化jvm参数也可以改善出现Full GC的概率,由于elasticsearch计算节点经常要在堆中分配大对象,为了解决分配大对象引来Full GC的问题,CMS垃圾收集器提供了一个可配置的参数,即-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程,内存整理的过程无法并发的,空间碎片问题没有了,但提顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。

参考

  1. JVM频繁Full GC的情况及应对策略
  2. Elasticsearch生产环境遇到的问题以及解决方案