ElasticSearch中的JVM性能调优

前一段时间被人问了个问题:在使用ES的过程中有没有做过什么JVM调优措施?

在我搭建ES集群过程中,参照important-settings官方文档来的,并没有对JVM参数做过多的调整。但谈到JVM配置参数,少不了操作系统层面上的一些配置参数,比如 page cache 和文件描述符的个数:(/etc/security/limits.conf)。另外ES jvm.options配置文件也针对JVM参数做了一些优化,这里简要介绍一下ElasticSearch中与jvm相关的各个配置参数:

  • 将 Xms 和 Xmx 设置成一样大 避免JVM堆的动态调整给应用进程带来"不稳定"。参考:Heap Tuning Parameters

    By default, the JVM grows or shrinks the heap at each GC to try to keep the proportion of free space to the living objects at each collection within a specific range. This range is set as a percentage by the parameters -XX:MinHeapFreeRatio=minimum and -XX:MaxHeapFreeRatio=maximum; and the total size bounded by -Xms and -Xmx

  • 预留足够的内存空间给page cache。假设一台32GB内存的机器,配置16GB内存给ES进程使用,另外16GB给page cache,而不是将 xmx 设置成32GB。

  • 禁用内存交换(/proc/sys/vm/swappiness),后面解释为什么要禁用内存交换?

  • 配置JVM参数:-XX:+AlwaysPreTouch 减少新生代晋升到老年代时停顿。JDK官方文档关于 AlwaysPreTouch 的解释是:Pre-touch the Java heap during JVM initialization. Every page of the heap is thus demand-zeroed during initialization rather than incrementally during application execution.
    也就是说:在启动时就把参数里说好了的内存全部都分配了,会使得启动慢上一点,但后面访问时会更流畅,比如页面会连续分配,比如不会在晋升新生代到老生代时才去访问页面,导致GC停顿时间加长

既然提到了这个参数,我想说一下Linux内存分配、Linux OOM Killer、内存交换vm.swappiness参数 之间的一些理解:当ES进程向操作系统MMU申请分配内存时,操作系统内核分配的是虚拟内存,比如指定 -Xms=16G 那么只是告诉内核这个ES进程在启动的时候最需要16G内存,但是ES进程在启动后并不是立即就用了16G的内存。因此,随着ES的运行,ES进程访问虚拟内存时产生缺页错误(page fault),然后内核为之分配实际的物理页面(这个过程也是需要开销的)。而如果在JVM启动时指定了AlwaysPreTouch,就会分配实际的物理内存,这样在发生YGC的时候,新生代对象晋升到老年代,减少老年代空间分配产生的缺页异常,从而减少YGC停顿时间。

正是由于Linux内存管理机制,应用程序进程可以申请比物理内存大得多的内存,而进程而言,它看到的是逻辑地址空间,因为通过内存交换(vm.swapiness),操作系统允许将那些长期不用的物理页面交换到磁盘上去,因此程序能够申请的内存空间范围是很大的。由于进程可以申请到一块很大的地址空间,但不一定把这些空间都使用了。但万一进程真的实实在在地占用了这么多空间,怎么办?于是OOM Killer 就出马了,OOM Killer 会选择一个进程将之杀死

在使用CMS垃圾回收器时,jmap -heap 查看jvm实际为ES进程分配的新生代的大小。因为,影响新生代大小的参数有三个:第一个是:-XX:NewSize和-XX:MaxNewSize、第二个是:-Xmn、第三个是:-XX:NewRatio=2。根据参数XX:NewRatio=2 ,ES 进程 jvm新生代堆大小占配置的总的堆的1/3,但其实并没有,就是因为参数:-XX:NewSize和-XX:MaxNewSize 优先级比XX:NewRatio=2高,覆盖了这个参数配置的值。

  • -XX:CMSInitiatingOccupancyFraction 设置成75%。主要是因为CMS是并发收集,垃圾回收线程和用户线程同时运行,用户线程运行时可能继续生成gc对象,如果到90%再去回收就太晚了。老年代使用到75%就回收可减少OOM异常发生的概率。

  • -XX:MaxTenuringThreshold 设置成6。这个值默认为15,即Survivor区对象经历15次Young GC后才进入老年代,设置成6就只需6次YGC就晋升到老年代了。默认情况下ES新生代采用 -XX:+UseParNewGC,老年代采用CMS垃圾回收器,关于这个参数的调优说明,可参考:关键业务系统的JVM参数推荐(2018仲夏版)

    Young GC是最大的应用停顿来源,而新生代里GC后存活对象的多少又直接影响停顿的时间,所以如果清楚Young GC的执行频率和应用里大部分临时对象的最长生命周期,可以把它设的更短一点,让其实不是临时对象的新生代对象赶紧晋升到年老代

  • -Xss 配置为1M。线程占用栈内存,默认每条线程为1M。从这个参数可看出一个进程下可创建的线程数量是有限制的,可视为创建线程的开销。

page cache 与 应用程序内存

Linux内存可分成2种类型:Page cache和应用程序内存。应用程序内存会被Linux的swap机制交换出去,而page cache则是由Linux后台的异步flush策略刷盘。当OS内存满了时,就需要把一部分内存数据写入磁盘,因此就需要决定是swap应用程序内存呢?还是清理page cache?OS有个系统参数/proc/sys/vm/swappiness默认值60,一般将之设置成0,表示优先刷新page cache而不是将应用程序的内存交换出去。

那Linux的page cache的清除策略是什么呢?--优化了的LRU算法。LRU存在缺点:那些新读取的但却只使用一次的数据占满了LRU列表,反而把热点数据给evict 到磁盘上去了,因此还需要考虑数据的访问次数(频率)

很多存储系统针对LRU做了一些优化,比如Redis的键过期策略是基于LRU算法的思想,用若干个位记录每个key的idle time(空闲时间),将空闲时间较大的key存入pool,从pool中选择key evict出去,具体可参考:Redis的LRU算法。 再比如Mysql innodb 缓冲池管理page也是基于LRU,当新读入一页数据时不是立即放到LRU链表头,而是设置mid point(默认37%),即放到链表37%位置处 。关于如何理解page cache 与应用程序内存之间的区别,可参考这篇文章:从Apache Kafka 重温文件高效读写

Linux总会把系统中还没被应用使用的内存挪来给Page Cache,在命令行输入free,或者cat /proc/meminfo,"Cached"的部分就是Page Cache。

当Linux系统内存不足时,要么就回收page cache,要么就内存交换(swap),而内存交换就有可能把应用程序的数据换出到磁盘上去了,这就有可能造成应用的长时间停顿了。参考:在你的代码之外,服务时延过长的三个追查方向(上)

Linux有个很怪的癖好,当内存不足时,有很大机率不是把用作IO缓存的Page Cache收回,而是把冷的应用内存page out到磁盘上。当这段内存重新要被访问时,再把它重新page in回内存(所谓的主缺页错误),这个过程进程是停顿的。增长缓慢的老生代,池化的堆外内存,都可能被认为是冷内存,用 cat /proc/[pid]/status 看看 VmSwap的大小, 再dstat里看看监控page in发生的时间。

Linux free 内存参数

free -m 查看机器的内存使用情况时,其实一直被以下几个概念所困扰。Google了一圈,大部分只是"翻译",没有解释。其实我是想了解这些参数对系统的影响是什么?
man free 查看注释如下,free 命令输出的内容其实就是 /proc/meminfo 中的内容,因此真正的是要理解 /proc/meminfo 中每一行的内容。
free displays the total amount of free and used physical and swap memory in the system, as well as the buffers and caches used by the kernel

  1. 如何理解操作系统的交换内存?
  2. 命令输出的 free 和 available 之间的区别是什么?free表示:Unused memory (MemFree and SwapFree in /proc/meminfo)。而available 一般都比free字段数值大,这是因为:available 还包含了那些已经被使用但是可以被回收的内存

一个JVM长时间停顿的示例

随着系统的运行,JVM堆内存会被使用,引用不可达对象就是垃圾对象,而操作系统有可能会把这些垃圾对象(长期未用的物理页面)交换到磁盘上去。当JVM堆使用到一定程度时,触发FullGC,FullGC过程中发现这些未引用的对象都被交换到磁盘上去了,于是JVM垃圾回收进程得把它们重新读回来到内存中,然而戏剧性的是:这些未引用的对象本身就是垃圾,读到内存中的目的是回收被丢弃它们!而这种来回读取磁盘的操作导致了FullGC 消耗了大量时间,(有可能)发生长时间的stop the world。因此,需要禁用内存交换以避免这种现象,这也是为什么在部署Kafka和ES的机器上都应该要禁用内存交换的原因吧(如果因长时间STW导致node与master之间的"心跳包"失效,集群的master节点认为该节点发生了故障,于是是自动进行failover,对于ES来说可能就发生自动rebalance分片迁移)。具体可参考这篇文章:Just say no to swapping!

总结

本文以ES使用的JVM配置参数为示例,记录了一些关于JVM调优的参数理解,以及涉及到的一些关于page cache、应用程序内存区别,LRU算法思想等,它们在Mysql、Kafka、ElasticSearch都有所应用。文中所引用的参考链接都非常好,对深入理解系统底层运行原理有帮助。
ElasticSearch官方文档HowTo也给出了ElasticSearch搜索、索引(Indexing)的调优方案,也值得一看:general-recommendations

一个统计gc日志的工具:gcplot,以图形化方式显示gc情况。(分析一个ES进程的gc日志得到:99%的gc STW 时间控制在77ms以内)

原文:https://www.cnblogs.com/hapjin/p/11135187.html

ElasticSearch中的JVM性能调优的更多相关文章

  1. JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码

    本文是<JVM 性能调优实战之:一次系统性能瓶颈的寻找过程> 的后续篇,该篇介绍了如何使用 JDK 自身提供的工具进行 JVM 调优将 TPS 由 2.5 提升到 20 (提升了 7 倍) ...

  2. 使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码 (jvm性能调优)

    技术交流群:233513714 本文是<JVM 性能调优实战之:一次系统性能瓶颈的寻找过程> 的后续篇,该篇介绍了如何使用 JDK 自身提供的工具进行 JVM 调优将 TPS 由 2.5 ...

  3. JVM性能调优实践——JVM篇

    前言 在遇到实际性能问题时,除了关注系统性能指标.还要结合应用程序的系统的日志.堆栈信息.GClog.threaddump等数据进行问题分析和定位.关于性能指标分析可以参考前一篇JVM性能调优实践-- ...

  4. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解

    摘要: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jstack.jmap.jhat.jstat.hprof等小巧的工具,本博客希望 ...

  5. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解(转VIII)

    JVM本身就是一个java进程,一个java程序运行在一个jvm进程中.多个java程序同时运行就会有多个jvm进程.一个jvm进程有多个线程至少有一个gc线程和一个用户线程. JDK本身提供了很多方 ...

  6. Java性能优化权威指南-读书笔记(二)-JVM性能调优-概述

    概述:JVM性能调优没有一个非常固定的设置,比如堆大小设置多少,老年代设置多少.而是要根据实际的应用程序的系统需求,实际的活跃内存等确定.正文: JVM调优工作流程 整个调优过程是不断重复的一个迭代, ...

  7. JVM 性能调优实战之:一次系统性能瓶颈的寻找过程

    玩过性能优化的朋友都清楚,性能优化的关键并不在于怎么进行优化,而在于怎么找到当前系统的性能瓶颈.性能优化分为好几个层次,比如系统层次.算法层次.代码层次…JVM 的性能优化被认为是底层优化,门槛较高, ...

  8. jvm 性能调优

    [转载]:http://blog.csdn.net/chen77716/article/details/5695893 最近因项目存在内存泄漏,故进行大规模的JVM性能调优 , 现把经验做一记录. 一 ...

  9. (转)JVM性能调优之生成堆的dump文件

    转自:http://blog.csdn.net/lifuxiangcaohui/article/details/37992725 最近因项目存在内存泄漏,故进行大规模的JVM性能调优 , 现把经验做一 ...

随机推荐

  1. Maven项目配置Logback输出JSON格式日志

    最近,项目提出需求,日志需要固定输出为JSON格式,以便后端Flink程序解析. 项目背景 项目为简单的Maven项目,日志由Filebeat采集,因此不需要配置输出至Logstash. 下面为pom ...

  2. pgsql主备搭建及切换

    二.主从搭建 2.1测试目标 测试postgresql主从搭建安装过程 2.2环境准备 实例级别的复制 流复制主库可读写,但从库只允许查询不允许写人, 而逻辑复制的从库可读写 流复制实验环境 主机 主 ...

  3. django rest framework 序列化组件总结

    序列化组件总结 一. 序列化组件本质上为了实现前后端分离,而进行json序列化的一个组件形式,极大方便了解析数据的作用 二. 所有序列化是基于APIView 解析器实现的,通过内部的多继承关系方便实现 ...

  4. JS三座大山再学习 ---- 原型和原型链

    本文已发布在西瓜君的个人博客,原文传送门 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式 ...

  5. Java多线程编程核心技术-第3章-线程间通信-读书笔记

    第 3 章 线程间通信 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大 ...

  6. LoadRunner开发http协议接口之form表单脚本

    loadrunner传form表单,用web_submit_data函数. Action() { // lr_start_transaction("hp_homepage"); / ...

  7. 【电脑】E470C如何关闭触摸板

    经查  以这种方式关闭最为简单. 若E470C没有这个模块,就装一个! http://www.edowning.net/soft/145089.htm#downbtn2

  8. Linux操作中应该注意的问题

    1.覆盖问题 (1)cp覆盖 (2)scp覆盖 (3)重定向 “>” 覆盖 (4)tar覆盖 2.磁盘问题 (1)GPT格式的分区会覆盖磁盘上原有的分区 (2)在/etc/fstab中写入完成后 ...

  9. [RN] React Native 使用 阿里 ant-design

    React Native 使用 阿里 ant-design 实例效果如图: 一.安装 npm install antd-mobile-rn --save npm install babel-plugi ...

  10. Android Studio 星云常用配置工具箱

    1. 安装插件 1.1 Android View绑定框架 开源地址:https://github.com/JakeWharton/butterknife 插件地址: https://github.co ...