Elasticsearch中关于JVM和垃圾回收的介绍

2021-01-05 14:38:59 浏览数 (1)

关于堆内存大小的设置

ES官方建议JVM中设置的最大堆内存大小,不超过节点RAM的一半,最大不超过32GB,并且XmsXmx相等。我们一个个说明。

首先为啥建议XmsXmx相等,这其实是个约定的配置了。并不仅限于ES,很多基于JAVA的服务在生产环境中都是建议这个配置。核心的原因在于减少JVM的时间开销,因为当Xms不够用时,JVM会继续想OS申请内存,这必然会带来额外的时间开销。如果你预测你的应用最终差不多用到Xmx,把这两个值设置成一样是个比较好的选择。

不超过系统RAM的一半,这个主要是出于文件系统缓存的考虑。我们知道Lucene的segment是一个个操作系统的文件,并且一但生成就不能更改,也就是只读的。所以操作系统也会把这些段文件缓存起来,以便更快的访问。这部分内存在查询量比较大的场景下,也会很快被lucene消耗掉。所以不用担心浪费的问题。反而如果操作系统的RAM过小,在ES查询并发比较大的时候,一个是查询比较慢,另外就是磁盘IO比较高。

关于不超过32GB的问题。这个问题要想搞明白,需要一些操作系统底层的知识储备。

原因是jvm在内存小于32G的时候会采用一个内存对象指针压缩技术。在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,比如4字节或者8字节,取决于你是32位的系统,还是64位的系统。

很多时候,如果只用32bit位往往不够,因为它最大只能表示到4GB的地址,但是生产环境中服务器内存往往大于这个数。直接用64bit表示呢,又很浪费空间,因为64bit最大可以表示的寻址空间为2的64次方bit,转化单位是

代码语言:javascript复制
2147483648GB。

另外一个知识点是,操作系统要求地址都必须是8的整数倍,这就是我们常听说的内存对齐。内次对齐这是主要是出于性能的考虑,CPU对于这样的地址访问更快。地址是8的倍数意味着低三位总是0。

JVM既然已经知道了这三位必然是0,就干脆把它利用起来,让这三个位也存储有意义的值。

在堆中是32位,和三个右移的0。在寄存器中用来寻址的依然是35位,2的35次方=32G。也就是说,使用32位,来达到35位oop所能引用的堆内存空间。

当JVM需要在内存中找到一个对象时,它将指针向左移动3位(内存对齐,寻址)。另一方面,当向堆加载指针时,JVM将指针向右移动3位以丢弃之前添加的零。

关于垃圾回收

ES默认的JVM配置使用的垃圾回收器是CMS,打开默认的配置文件,通常能看到如下的配置:

代码语言:javascript复制
-XX: UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX: UseCMSInitiatingOccupancyOnly

第一行表示垃圾回收使用CMS,第二行表示指设定CMS在对老年代占用率达到75%的时候开始GC,避免老年代占满触发fullGC。

UseCMSInitiatingOccupancyOnly一般和上面那个配合一起使用,设定的回收阈值(上面指定的75%),如果不指定,JVM仅在第一次使用设定值,后续则自动调整。

通常在生产上,我们的服务器堆内存都配合的比较大(一般都会超过16G)。官方建议使用G1垃圾回收。因为这种大堆内存的场景,CMS一般等到老年代占用很大的时候进行回收,停顿时间太长,这对大部分应用程序是致命的。

而G1相对更加智能,对停顿时间可以预测,对于大堆回收垃圾的效率更高。下面是在一篇国外的博客上找到的二者在同一个ES集群环境中测试的对比图,上面是使用CMS的测试结果,下面是G1的测试结果,你可以感受下:

0 人点赞