目录
(二)配置Elasticsearch堆内存时,将初始大小设置为物理内存的一半(重点理解)
(一)Java 8 jvm.options文件中的JVM日志参数配置
(二)Java 9+ jvm.options文件中的JVM日志参数配置
(一)Java 8中Elasticsearch默认使用CMS垃圾回收器
(二)-XX:-OmitStackTraceInFastThrow
Java 8目前仍然是许多企业中主要使用的版本之一,尤其是对于比较保守的公司。在过去,CMS (Concurrent Mark-Sweep) 垃圾回收器在Java 8中是一种常见选择,因为它在某些场景下能够提供较好的性能。
然而,随着Java版本的不断更新,一些旧的特性和组件被淘汰或替代,比如CMS。Java 14中正式废弃了CMS,而新的垃圾回收器,如ZGC和G1,逐渐成为了主流选择。ZGC和G1在处理大内存堆和低停顿时间方面表现出色,适用于现代应用程序的需求。
另外,自Java 9以后,Java的发布模式也发生了变化,从长期支持(LTS)版本切换到了更频繁的发布,大约每六个月发布一次。Java 8和Java 11是目前支持的LTS版本,它们提供了更长时间的支持和维护,适合希望保持稳定性和兼容性的企业和组织使用。
关于JVM相关的优化和配置我们之前提到过很多基本的知识内容,比如如下的几篇重点内容,有时间可以在回顾一下:
涉猎内容 | 具体链接 |
基本知识内容 | Java回收垃圾的基本过程与常用算法_java垃圾回收过程-CSDN博客 |
CMS调优和案例分析 | CMS垃圾回收器介绍与优化分析案列整理总结_cms 对老年代的回收做了哪些优化设计-CSDN博客 |
G1调优分析 | Java Hotspot G1 GC的理解总结_java g1-CSDN博客 |
ZGC基础和调优案例分析 | 垃圾回收器ZGC应用分析总结-CSDN博客 |
高频面试题汇总 | JVM高频基本面试问题整理_jvm面试题-CSDN博客 |
今天我们就JVM常见优化参数为基本内容再次重新来说(主要从ES的JVM配置来强化理解)。
一、真实查看参数
JVM和垃圾回收器的参数配置是一个复杂而且经常变化的领域。很多时候,我们可能会尝试通过调整参数来优化应用程序的性能,但是如果不了解这些参数的默认值和影响,就很容易陷入误区。
在查看自己的应用真实的JVM生效参数上,我们需要先通过一下基本命令行进行了解:
(一)-XX:+PrintCommandLineFlags
-XX:+PrintCommandLineFlags
可以打印出JVM使用的命令行标志,包括垃圾回收器类型和一些默认值。具体使用如下:
java -XX:+PrintCommandLineFlags -version
输出事例如下:
-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
openjdk version "1.8.0_41"OpenJDK Runtime Environment (build 1.8.0_41-b04)
OpenJDK 64-Bit Server VM (build 25.40-b25, mixed mode)
简单看起信息
参数 | 说明 |
---|---|
-XX=268435456 | 初始堆大小为 256 MB |
-XX=4294967296 | 最大堆大小为 4 GB |
-XX:+PrintCommandLineFlags | 打印命令行标志,即启动JVM时使用的所有参数 |
-XX:+UseCompressedClassPointers | 使用压缩类指针 |
-XX:+UseCompressedOops | 使用压缩普通对象指针 |
-XX:-UseLargePagesIndividualAllocation | 禁用大页面单独分配 |
-XX:+UseParallelGC | 使用并行垃圾回收器 |
对于Java版本信息就不必解释了吧。
(二)-XX:+PrintFlagsFinal
当你在调优应用程序时,可能会试着添加一些JVM参数来提高性能。有时候,你可能会觉得添加了某个参数后,程序的运行速度明显提升了。但是,当你通过 -XX:+PrintFlagsFinal
命令来查看参数的默认值时,可能会发现这个参数的默认配置和你所设置的值并不一样,改值可能默认是不能修改的。
因此,在调整JVM参数时,特别是在不同的JVM版本和垃圾回收器上,我们应该先查看该参数的默认配置,而不是轻信他人的建议。可以通过以下命令来查看某个参数的默认值,比如使用G1垃圾回收器时是否启用了自适应大小策略:
java -XX:+PrintFlagsFinal -XX:+UseG1GC 2>&1 | grep UseAdaptiveSizePolicy
这样就能更准确地了解参数的影响,避免因为不了解默认配置而产生误解。
二、堆空间的配置
Elasticsearch是一个基于Java开发的高性能开源分布式搜索引擎,因此它的运行依赖于Java虚拟机(JVM)。在Elasticsearch的conf目录下,有一个名为jvm.options的文件,jvm.options文件是配置Elasticsearch JVM参数的地方,通过合理地配置这些参数,可以更好地发挥Elasticsearch的性能和稳定性。我们本次就堆空间配置进行学习。
(一)默认配置
JVM 的内存,分为堆和堆外内存,其中堆的大小可以通过 Xmx 和 Xms 来配置。
以下是一个简化的jvm.options文件示例,来看一下Elasticsearch官方提供的默认堆空间配置:
# Elasticsearch jvm.options configuration
# 设置初始堆大小和最大堆大小为1GB
-Xms1g
-Xmx1g
# AlwaysPreTouch参数配置
-XX:+AlwaysPreTouch
将Elasticsearch堆空间配置整合成表格:
参数 | 说明 |
---|---|
-Xms1g | 初始堆大小为1GB |
-Xmx1g | 最大堆大小为1GB,限制堆空间不超过1GB |
-XX:+AlwaysPreTouch | 在JVM启动时预先分配所有指定的堆内存,减少运行时的内存动态分配性能损耗 |
通过这些配置,Elasticsearch在启动时会使用1GB的初始堆空间,并且预先分配所有指定的堆内存,以避免运行时内存动态分配的性能损耗。
(二)配置Elasticsearch堆内存时,将初始大小设置为物理内存的一半(重点理解)
当配置Elasticsearch堆内存时,我们通常把堆的初始化大小设置成物理内存的一半。其考虑点主要是因为一下几点原因:
-
文件缓存需求:作为存储型服务,Elasticsearch需要处理大量的数据和文件。为了提高文件的读取效率,特别是频繁访问的文件,需要使用文件缓存(Page Cache)。这样可以避免频繁地从磁盘读取文件,提高系统的性能。然而,文件缓存需要占用一定的内存空间。因此,为了确保有足够的内存空间给文件缓存,通常会将堆的初始大小设置为物理内存的一半。
-
避免内存过度分配:将堆的初始大小设置为物理内存的一半,可以避免过度占用系统内存资源,从而确保系统有足够的内存空间来执行其他任务和进程,可以提高系统的整体稳定性和性能。
-
减少内存碎片化:堆内存的动态分配和释放可能导致内存碎片化,影响系统的性能。通过合理设置堆的初始大小,可以减少堆空间的动态调整,降低内存碎片化的程度,提高系统的稳定性和性能。
但注意对于计算型节点,例如普通的Web服务,通常会采用将堆内存设置为物理内存的2/3,留下的1/3用于堆外内存使用。其考虑点主要是因为一下几点原因:
-
提高堆内存的利用率:将堆内存设置为物理内存的2/3,可以充分利用系统的内存资源,提高堆内存的利用率。这样可以确保在执行计算密集型任务时,有足够的内存空间来存储和处理数据,从而提高系统的性能和响应速度。
-
保留堆外内存空间:堆外内存通常用于存储较大的数据结构或者缓存数据,例如直接内存(Direct Memory)用于NIO操作、off-heap存储等。将物理内存的1/3留作堆外内存使用,可以确保系统在需要时有足够的内存空间来存储和处理这些数据,同时避免由于堆内存占用过多而导致内存不足的问题。
-
提高系统的稳定性和可靠性:通过合理配置堆内存和堆外内存的比例,可以更好地平衡系统的内存资源,避免过度分配或者不足的情况发生,从而提高系统的稳定性和可靠性。
请注意以上这两点的主要区分内容。
(三)堆外内存划分说明
元空间(Metaspace)
元空间是用于存储类元数据的区域,例如类的结构、方法信息等。
通过设置参数 -XX:MaxMetaspaceSize
和 -XX:MetaspaceSize
,可以指定元空间的最大内存和初始化内存。
由于元空间默认是没有上限的,极端情况下,它可能会一直扩张占用操作系统剩余的内存。
JIT编译后代码存放
JIT(Just-In-Time)编译器会将Java字节码编译为本地机器代码,并存放在Code Cache中。这些代码在运行时被执行。
可以通过参数 -XX:ReservedCodeCacheSize
来限制Code Cache的大小。此外,JNI(Java Native Interface)的代码也存放在Code Cache中。
本地内存
本地内存是指其他附加在JVM进程上的内存区域,例如网络连接占用的内存、线程创建占用的内存等。在高并发应用中,这些内存累积起来可能会相当可观。
直接内存
直接内存是通过ByteBuffer类申请的,它会绕过JVM的堆内存,直接在操作系统的本地内存中分配空间。可以通过参数 -XX:MaxDirectMemorySize
来限制直接内存的大小。
JNI内存
JNI内存指的是由JNI代码通过malloc等方式在堆外分配的内存。不幸的是,JVM无法控制这部分内存的使用,它依赖于具体的JNI代码实现。
(四)平常的应用服务较常用的调整分代比例
在平常的应用服务中,我们希望得到更细粒度的控制,其中比较常用的就是调整各个分代之间的比例。
- -Xmn 年轻代大小,默认年轻代占堆大小的 1/3。高并发快消亡场景可适当加大这个区域,对半或者更多都是可以的。但是在 G1 下,就不用再设置这个值了,它会自动调整;
- -XX:SurvivorRatio 默认值为 8,表示伊甸区和幸存区的比例;
- -XX:MaxTenuringThreshold 这个值在 CMS 下默认为 6,G1 下默认为 15。这个值和我们前面提到的对象提升有关,改动效果会比较明显。对象的年龄分布可以使用 -XX:+PrintTenuringDistribution 打印,如果后面几代的大小总是差不多,证明过了某个年龄后的对象总能晋升到老年代,就可以把晋升阈值设的小一些;
- PretenureSizeThreshold 超过一定大小的对象,将直接在老年代分配,不过这个参数用得不是很多。
三、日志参数配置
在Java 9之前,JVM的日志输出主要依赖于选项-Xloggc
和-XX:+PrintGC
等参数。这些参数的配置方式相对固定,并且在不同的Java版本中保持一致。但由于Java 9引入了JEP 158,即Unified JVM Logging(重新设计和重构了JVM的日志系统),提供了更丰富的选项和功能,包括不同类型日志的过滤、格式化、标签等,使用了-Xlog
参数来配置日志输出。
为了适应Java 9引入的新日志系统,Elasticsearch在jvm.options文件中针对Java 9以上版本采用了新的参数配置方式,而对于Java 8则保留了之前的参数配置方式,以保持与旧版Java兼容。
(一)Java 8 jvm.options文件中的JVM日志参数配置
# Elasticsearch 7.x JVM日志参数配置 (Java 8)
# 打印GC的详细信息
-XX:+PrintGCDetails
# 打印GC发生的时间戳
-XX:+PrintGCDateStamps
# 打印对象年龄分布
-XX:+PrintTenuringDistribution
# 在GC期间应用程序停止的时间
-XX:+PrintGCApplicationStoppedTime
# 将GC日志输出到指定文件
-Xloggc:logs/gc.log
# 启用GC日志文件轮换
-XX:+UseGCLogFileRotation
# GC日志文件的数量
-XX:NumberOfGCLogFiles=32
# 单个GC日志文件的大小
-XX:GCLogFileSize=64m
(二)Java 9+ jvm.options文件中的JVM日志参数配置
# Elasticsearch 7.x JVM日志参数配置 (Java 9+)
# 这是Java 9及以上版本的新格式,使用了-Xlog参数,用于配置GC日志输出。
# 这个参数配置了不同类型的GC日志输出,包括gc、gc+age、safepoint,并将日志输出到指定的文件中。
# 参数filecount=32指定了GC日志文件的数量,filesize=64m指定了单个GC日志文件的大小。
-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m
具体说明可见上方配置注释标注。
四、异常日志记录
在Elasticsearch的jvm.options文件中,针对异常情况记录的JVM参数配置示例:
# Elasticsearch JVM异常情况记录参数配置
# 在内存溢出时生成堆转储文件
-XX:+HeapDumpOnOutOfMemoryError
# 指定堆转储文件的保存路径
-XX:HeapDumpPath=data
# 指定错误日志文件的保存路径
-XX:ErrorFile=logs/hs_err_pid%p.log
在Java应用程序的运行过程中,常见的异常情况如内存溢出(OutOfMemoryError,OOM),其可能导致应用程序崩溃或性能下降。为了有效地诊断和解决这类问题,以上这些参数用于在异常情况下记录堆转储文件(Heap Dump)和错误日志文件(Error Log),以便在发生问题时进行分析和诊断。
(一)HeapDumpOnOutOfMemoryError
用于指示在发生内存溢出错误时自动生成堆转储文件(Heap Dump)。堆转储文件是Java堆的快照,记录了在程序运行期间分配的所有对象以及它们的引用关系。通过分析堆转储文件,可以深入了解内存使用情况,找到内存泄漏或者其他造成内存溢出的原因。
(二)HeapDumpPath
用于指定生成的堆转储文件的保存路径。一般情况下,堆转储文件会比较大,因此需要将它们保存在一个足够大的磁盘空间中,以便后续的分析和处理。
(三)ErrorFile
用于指定错误日志文件的保存路径。当Java应用程序发生严重错误时(如虚拟机错误或崩溃),会生成错误日志文件,其中包含了错误的详细信息和堆栈跟踪。这些信息对于分析和排查问题都非常重要。
(四)作用和意义
配置这些参数可以帮助我们在发生内存溢出或其他严重错误时快速定位问题。一旦发生OOM错误,JVM就会自动在指定路径下生成堆转储文件,我们可以使用内存分析工具(如MAT)来打开这些文件,深入分析内存使用情况,找到问题的根源。同时,错误日志文件也可以提供有用的调试信息,帮助我们了解错误发生的原因和上下文。
五、垃圾回收器配置
(一)Java 8中Elasticsearch默认使用CMS垃圾回收器
典型的Elasticsearch 7.x版本中,Elasticsearch的默认配置确实使用了CMS(Concurrent Mark-Sweep)垃圾回收器,尤其是在Java 8环境中。在Elasticsearch的默认配置文件jvm.options
中,你可以看到CMS相关的JVM参数配置:
## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
- UseConcMarkSweepGC,表示年轻代使用 ParNew,老年代的用 CMS 垃圾回收器
- -XX:CMSInitiatingOccupancyFraction 由于 CMS 在执行过程中,用户线程还需要运行,那就需要保证有充足的内存空间供用户使用。如果等到老年代空间快满了,再开启这个回收过程,用户线程可能会产生“Concurrent Mode Failure”的错误,这时会临时启用 Serial Old 收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了(STW)。
-
-XX:+UseCMSInitiatingOccupancyOnly,
只有在堆空间使用率达到指定阈值时才触发CMS垃圾回收。此参数必须与-XX:CMSInitiatingOccupancyFraction
配合使用,以确保CMS只在指定的阈值时启动。
另外,对于 CMS 垃圾回收器,常用的还有下面的配置参数:
- -XX:ExplicitGCInvokesConcurrent 当代码里显示的调用了 System.gc(),实际上是想让回收器进行FullGC,如果发生这种情况,则使用这个参数开始并行 FullGC。建议加上。
- -XX:CMSFullGCsBeforeCompaction 默认为 0,就是每次FullGC都对老年代进行碎片整理压缩,建议保持默认。
- -XX:CMSScavengeBeforeRemark 开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试。可以降低 remark 时间,建议加上。
- -XX:+ParallelRefProcEnabled 可以用来并行处理 Reference,以加快处理速度,缩短耗时。
CMS 垃圾回收器,已经在 Java14 中被移除,由于它的 GC 时间不可控,有条件应该尽量避免使用。
(二)Java 9及以上版本垃圾回收器变化
对于Java 9及以上版本,由于垃圾回收器和日志系统的变化,Elasticsearch的jvm.options
文件也会有所不同,使用新的-Xlog
参数配置GC日志,并可能使用不同的垃圾回收器(如G1 GC):
## G1 GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=45
-XX:+ParallelRefProcEnabled
在这里,使用了G1垃圾回收器,并配置了相应的参数来优化其行为。Elasticsearch可以在不同的Java版本中,利用适当的垃圾回收策略和日志记录方式来保证其性能和稳定性。
主要的配置参数:
- -XX:MaxGCPauseMillis 设置目标停顿时间,G1 会尽力达成。
- -XX:G1HeapRegionSize 设置小堆区大小。这个值为 2 的次幂,不要太大,也不要太小。如果是在不知道如何设置,保持默认。
- -XX:InitiatingHeapOccupancyPercent 当整个堆内存使用达到一定比例(默认是45%),并发标记阶段就会被启动。
- -XX:ConcGCThreads 并发垃圾收集器使用的线程数量。默认值随 JVM 运行的平台不同而不同。不建议修改。
号外:JVM 支持非常多的垃圾回收器,有兴趣可查看其他资料,重点的三个在开篇已有推荐。
六、额外自定义参数
额外的自定义参数通常用于调整和优化Elasticsearch的性能、稳定性和安全性,以满足特定的应用场景和需求。这些参数可能涉及到底层的Java虚拟机(JVM)、网络设置、日志系统以及其他相关的配置。以下是这些参数在Elasticsearch jvm.options
文件中的示例配置:
## 设置每个Java虚拟机栈的容量
-Xss1m
## 确保异常打印完整堆栈信息
-XX:-OmitStackTraceInFastThrow
## 启用Headless模式
-Djava.awt.headless=true
## Java 9及以上版本的参数
9-:-Djava.locale.providers=COMPAT
## 设置文件编码为UTF-8
-Dfile.encoding=UTF-8
## 设置网络地址缓存时间
-Des.networkaddress.cache.ttl=60
-Des.networkaddress.cache.negative.ttl=10
## Netty相关参数
-Dio.netty.noUnsafe=true
-Dio.netty.noKeySetOptimization=true
-Dio.netty.recycler.maxCapacityPerThread=0
## Log4j相关参数
-Dlog4j.shutdownHookEnabled=false
-Dlog4j2.disable.jmx=true
## 设置临时目录路径
-Djava.io.tmpdir=${ES_TMPDIR}
## 禁用JNA系统调用
-Djna.nosys=true
我们选取几个简单说一下。
(一)-Xss
参数
-Xss
参数用于设置每个线程的Java虚拟机栈(JVM Stack)的大小。JVM栈是线程私有的内存区域,用于存储线程执行方法时的局部变量、方法参数、方法调用和返回信息等。栈的大小会影响到线程的最大深度,即线程可以嵌套调用的方法数量。
在默认情况下,每个线程的JVM栈大小由 -XX:ThreadStackSize
参数决定,而 -Xss
参数则是设置所有线程的JVM栈大小,其值会覆盖 -XX:ThreadStackSize
的设定。在具体使用时,两者是等价的,只是 -Xss
更为简洁。
例如, -Xss1m
表示将每个线程的JVM栈大小设置为1MB。这意味着每个线程在执行时最多只能使用1MB的栈空间。如果应用程序的线程数量较多,且每个线程的方法调用深度较大,那么设置较小的栈大小可能会导致栈溢出(StackOverflowError)异常。
在配置该参数时,需要根据具体的应用场景和需求进行合理的调整。如果应用程序的方法调用深度较大或者并发线程较多,可以适当增加JVM栈的大小以避免栈溢出问题;反之,如果线程数量较少或者方法调用深度不大,可以适当减小JVM栈的大小以节省内存空间。
(二)-XX:-OmitStackTraceInFastThrow
用于调整异常处理行为的JVM参数。当设置为 -XX:-OmitStackTraceInFastThrow
时,它会关闭快速异常抛出时省略堆栈信息的优化,即在发生异常时完整地打印异常的堆栈信息。
在默认情况下,JVM会对某些频繁抛出的异常(如 NullPointerException
、ArrayIndexOutOfBoundsException
等)进行优化,省略异常堆栈信息以提高性能。这种优化能够减少异常抛出时的开销,但也使得调试时定位问题变得更加困难,因为无法得知异常发生的具体位置和上下文信息。
但需要注意的是,关闭异常快速抛出的优化会带来一定的性能损耗,因为在抛出异常时需要额外的开销来生成完整的堆栈信息。因此,在生产环境中,可能需要权衡性能和调试方便性之间的平衡,根据具体情况来决定是否关闭该优化。
(三)-Djava.awt.headless
-Djava.awt.headless=true
是一个Java系统属性,用于在启动Java虚拟机(JVM)时设置系统参数。这个参数的作用是告诉Java虚拟机当前运行的环境是否是 Headless 模式。
在Headless模式下,操作系统缺少了显示设备、键盘或鼠标等交互设备,因此Java应用程序需要使用软件方式来模拟这些设备。这种模式通常用于服务器环境,因为在服务器上通常没有图形界面或人机交互设备,只需要执行后台任务,如处理数据、运行服务等。
设置 -Djava.awt.headless=true
后,Java虚拟机将以无图形界面的方式运行,不会尝试去创建图形界面相关的窗口、组件或事件。这有助于提高性能和减少资源占用,因为不需要加载图形相关的库和模块,也不会尝试与图形系统交互。
在一些场景下,特别是服务器端的Java应用程序,启用Headless模式可以提高系统的稳定性和性能。这样的应用程序通常是以后台服务的形式运行,不需要图形界面,因此启用Headless模式可以更好地适应环境并节省系统资源。
七、分布式列存数据库Cassandra JVM参数简单回顾
Cassandra 是一个高性能的分布式列存数据库,广泛应用于需要高吞吐量和低延迟的大规模数据存储和检索场景。Cassandra 使用 Gossip 协议进行节点间通信和集群状态维护。
Cassandra 的 JVM 参数配置文件 jvm.options
每个参数配置如下:
# 堆内存设置
-Xms4G
-Xmx4G
# GC 日志配置
-XX:+UseG1GC
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintPromotionFailure
-Xloggc:/var/log/cassandra/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=10M
# 性能优化
-XX:+AlwaysPreTouch
-XX:+UseTLAB
-XX:+ResizeTLAB
-XX:+PerfDisableSharedMem
# G1GC 特定配置
-XX:MaxGCPauseMillis=500
-XX:G1HeapRegionSize=16M
-XX:InitiatingHeapOccupancyPercent=70
# 线程栈设置
-Xss256k
# 异常处理
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/lib/cassandra/java_oom.hprof
-XX:ErrorFile=/var/log/cassandra/hs_err_pid%p.log
# JMX 配置
-Dcom.sun.management.jmxremote.port=7199
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
# 本地配置
-Djava.library.path=/usr/share/cassandra/lib/sigar-bin
-Dcassandra.jmx.local.port=7199
# 头部模式
-Djava.awt.headless=true
标签:起步,Java,XX,参数,内存,JVM,日志,ES
From: https://blog.csdn.net/xiaofeng10330111/article/details/139639510