文档摘录来自ES大佬 铭毅天下
宿主机内存的一半和31GB,两个值中,取最小值。
Java中的堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实例对象。在Java中,堆被划分成两个不同的区域:新生代(Young)、老年代(Old)。
新生代(Young)又被划分为三个区域:Eden、From Survivor、To Survivor。
这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包括内存的分片以及回收。
在虚拟机启动时创建。
堆内存的 唯一目的就是创建对象实例 ,所有的对象实例和数组都要在堆上分配。
堆是由垃圾回收来负责的,因此也叫做"GC堆",垃圾回收采用分代算法,堆由此分为新生代和老生代。
堆的优势是可以动态地分配内存大小 ,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。
但 缺点 是,由于要在运行时动态分配内存,存取速度较慢。当堆内存因为满了无法扩展的时候就会抛出java.lang.OutOfMemoryError:Java heap space异常。出现这种情况的解决办法具体参见java调优。
默认情况下,ES JVM使用堆内存最小和最大大小为2GB(5.X版本以上)。
早期版本默认1GB,官网指出:这明显不够。
在转移到生产环境时,配置足够容量的堆大小以确保ES功能和性能是必要的。
ES将通过Xms(最小堆大小)和Xmx(最大堆大小)设置来分配jvm.options中指定的整个堆。
堆内存对于ES绝对重要。它被许多内存数据结构用来提供快速 *** 作。但还有另外一个非常重要的内存使用者:Lucene.
Lucene旨在利用底层 *** 作系统来缓存内存中的数据结构。Lucene段(segment)存储在单个文件中。因为段是一成不变的,所以这些文件永远不会改变。这使得它们非常容易缓存,并且底层 *** 作系统将愉快地将热段(hot segments)保留在内存中以便更快地访问。这些段包括倒排索引(用于全文搜索)和文档值(用于聚合)。
Lucene的性能依赖于与 *** 作系统的这种交互。 但是如果我们把所有可用的内存都给了ES的堆内存,那么Lucene就不会有任何剩余的内存 。这会严重影响性能。
标准建议是将可用内存的50%提供给ES堆,而将其他50%空闲。它不会被闲置,Lucene会高兴地吞噬掉剩下的东西。
如果我们不需要在字段上做聚合 *** 作(例如,我们不需要fielddata),则可以考虑进一步降低堆。堆越小,我们可以从ES(更快的GC)和Lucene(更多内存缓存)中获得更好的性能。
在Java中,所有对象都分配在堆上并由指针引用。普通的对象指针(OOP)指向这些对象,传统上它们是CPU本地字的大小:32位或64位,取决于处理器。
对于32位系统,这意味着最大堆大小为4GB。对于64位系统,堆大小可能会变得更大, 但是64位指针的开销意味着仅仅因为指针较大而存在更多的浪费空间 。并且比浪费的空间更糟糕的是,当在主存储器和各种缓存(LLC,L1等等)之间移动值时,较大的指针 消耗更多的带宽 。
Java使用称为压缩OOPS的技巧来解决这个问题。而不是指向内存中的确切字节位置,而是表示指针引用对象偏移量。这意味着一个32位指针可以引用40亿个对象,而不是40亿个字节。最终,这意味着堆可以增长到约32GB的物理尺寸,同时仍然使用32位指针。
一旦穿越了这个神奇的32GB的边界,指针就会切换回普通的对象指针。每个指针的大小增加,使用 更多的CPU内存带宽 ,并且实际上会丢失内存。实际上,在使用压缩OOPS获得32GB以下堆的相同有效内存之前,需要大约40-50GB的分配堆。
以上小结为:即使我们有足够的内存空间,尽量避免跨越32GB的堆边界。
否则会导致浪费了内存,降低了CPU的性能,并使GC在大堆中挣扎。
首先,我们建议避免使用这种大型机器。
但是,如果已经有了这些机器,我们有三种实用的选择:
方式一:最好的办法是在系统上完全禁用交换分区。
下面可以暂时关闭swap:
要永久禁用它,需要编辑我们的/etc/fstab
方式二:控制 *** 作系统尝试交换内存的积极性。
如果完全禁用交换不是一种选择,我们可以尝试降低swappiness。该值控制 *** 作系统尝试交换内存的积极性。这可以防止在正常情况下交换,但仍然允许 *** 作系统在紧急内存情况下进行交换。
对于大多数Linux系统,这是使用sysctl值配置的:
1的swappiness优于0,因为在某些内核版本上,swappiness为0可以调用OOM杀手。
方式三:mlockall允许JVM锁定其内存并防止其被 *** 作系统交换
最后,如果两种方法都不可行,则应启用mlockall文件。这允许JVM锁定其内存并防止其被 *** 作系统交换。在我们的elasticsearch.yml中,设置如下:
wood大叔:事实上,给ES分配的内存有一个魔法上限值26GB。这样可以确保启用zero based Compressed OOPS,这样性能才是最佳的。
Elasticsearch是基于Java构建的,需要至少 Java8 来运行它。只支持Oracle的Java和OpenJDK。所有Elasticsearch节点和客户机都应该使用相同的JVM版本。
我们推荐您安装Java1.8.0_131版本或者Java 8发行版系列的后续版本。我们推荐您使用 LTS JAVA 版本。如果使用了已知的糟糕的Java版本,Elasticsearch将拒绝启动。
Elasticsearch将使用的Java版本可以通过设置JAVA_HOME环境变量进行配置。
默认情况下,Elasticsearch告诉JVM使用最小和最大大小为1 GB的堆。 在转移到生产环境时,重要的是配置堆大小,以确保Elasticsearch有足够的可用堆。
Elasticsearch将通过Xms(最小堆大小)和Xmx(最大堆大小)设置分配在jvm.options文件中指定的整个堆。
这些设置的值取决于服务器上可用RAM的数量。好的经验法则是:
显示启用了从零开始的压缩oops而不是:
下面是如何通过jvm.options文件设置堆大小的例子:
还可以通过环境变量设置堆大小。这可以通过注释掉jvm.options文件中的Xms和Xmx设置来实现并通过ES_JAVA_OPTS设置这些值:
注意: 为Windows服务配置堆与上述配置不同。Windows服务最初填充的值可以如上配置,但在安装服务之后会有所不同。有关更多细节,请参阅 Windows服务文档 。
默认情况下,Elasticsearch配置JVM将堆从内存溢出异常转储到默认数据目录(/var/lib/elasticsearch是针对RPM和Debian包发行版的,Elasticsearch安装根目录下的data目录是针对tar和zip存档发行版的)如果此路径不适合接收堆转储,则应修改条目 -XX:HeapDumpPath=… 在jvm.options文件中。如果指定目录,JVM将根据运行实例的PID为堆转储生成一个文件名。如果指定的是固定文件名而不是目录,那么当JVM需要对内存溢出异常执行堆转储时,文件必须不存在,否则堆转储将失败。
默认情况下,Elasticsearch启用GC日志。这些都是在jvm.options中配置的和默认设置到与Elasticsearch日志相同的默认位置。默认配置每64 MB旋转日志一次,最多可以消耗2 GB的磁盘空间。
默认情况下,Elasticsearch配置JVM将致命错误日志写入默认日志目录(/var/log/elasticsearch是RPM和Debian包发行版的,Elasticsearch安装根目录下的logs目录是针对tar和zip存档发行版的)。这些日志是JVM遇到致命错误(例如,分割错误)时生成的。如果这个路径不适合接收日志,您应该在jvm.options文件中修改条目 -XX:ErrorFile=… 为一个替代路径。
确保将对内存最大值和最小值设置为相同大小,防止出现在运行时改变堆内存大小而消耗系统资源;
lucene会消耗大量的非堆内存,其被设计可以很好利用 *** 作系统的底层缓存机制,从而提升检索性能。标准建议将50%可用内存作为es的堆内存,剩下50%交给 *** 作系统及lucene,如不考虑对分词字符串的聚合 *** 作(不使用fielddata),可以降低对内存,这样对es及lucene的性能都会提升;
同时对内存最大不要超过32GB,Java 使用一个叫作 内存指针压缩(compressed oops)的技术来解决这个问题。 它的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。这意味着 32 位的指针可以引用 40 亿个 对象 , 而不是 40 亿个字节。最终, 也就是说堆内存增长到 32 GB 的物理内存,也可以用 32 位的指针表示。
一旦你越过那个神奇的 ~32 GB 的边界,指针就会切回普通对象的指针。 每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。事实上,当内存到达 40–50 GB 的时候,有效内存才相当于使用内存对象指针压缩技术时候的 32 GB 内存。
内存交换到磁盘对服务器性能来说是致命的,最好的办法就是在你的 *** 作系统中完全禁用 swap。可以设置配置文件中的 mlockall 开关, 它的作用就是允许 JVM 锁住内存,禁止 *** 作系统交换出去。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)