JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。

读书心得:乐观积极面对挑战,能力与挑战难度相匹配,专注享受当下事宜,无视周边因素,达到忘我状态,时间也会忘记。

    不管是刚入行没多久的JAVA初级级研发还是10年资深架构师,手上都需要珍藏一份JVM参数指南。不仅是为了学习JVM调优,也是时刻方便在实际工作中对业务系统的调优监控做出及时调整。本文全面整理JVM参数清单,并做成表格,以及公开一份大厂生产环境核心常用的调优模板供大家参考应用,希望对有缘刷到的同学有所帮助。

    上一篇文章,末尾我们给了一份jvm参数清单并留了一个问题,说说每个参数的意义?看完本文就能得到完整的答案(坚持授人以鱼,不如授人以渔的原则),尽量不填鸭投喂,希望和大家一起思考提升进步。

一、JVM参数有多少种类型?

    这个分类没有标准答案,大家根据各自理解和应用,会有不同分类。我们采用从参数开头、区域功能类型两大类进行划分,更加贴近实际应用,也方便记忆和学习。

二、按参数开头划分

    首先,大家看一下这份jar包启动参数命令集,先不用想具体含义,看看参数都有哪些特点。

    这里看到里面有的是-X开头,有的是-XX开头,有的是-D开头,有的是-开头。这里我们把-开头的,叫做【标准参数】,-X开头的叫做【非标准参数】,-XX开头的叫做【非稳定参数】。

2.1 标准参数(-开头)

    -开头的标准参数,我们常见的有:-jar,-help,-version,以及-agentpath:[=<选项>]、-javaagent:[=<选项>]都是。

2.1.1 如何查看全部标准参数列表?

    可以通过java -help命令去查看标准参数清单。

2.2 非标准参数(-X开头)

    -X开头的非标准参数,随着JDK的版本变化,参数的变化也很少。这里就有很多熟悉的参数,具体参数含义我们在文末表格里详细说明。这里简略说几个,比如以下三个。

-Xms :设置初始 Java 堆大小;
-Xmx  :设置最大 Java 堆大小;
-Xss   设置 Java 线程堆栈大小;

2.2.1 那如何查看-X开头的非标准参数,如何整理全部JVM参数清单?

    其实也简单,类似非标准参数java -help一样,只要执行java -X 就可以看到全部非标准参数。

2.3 不稳定(UnStable)参数(-XX开头)

     我们常说的JVM调优,应用最多的就是这个-XX开头的参数,它也是最为复杂。而且不同JDK版本、应用不同的垃圾回收器参数都各不相同,非常值得我们深入学习探讨。这些非稳定参数,有的用来选择GC回收器类型、有的用来控制GC日志、有的用来控制GC频率、内存占比划分。比如:


设置年老年用CMS垃圾回收器的-XX:+UseConcMarkSweepGC;
设置年轻代存活对象进入老年代的年龄-XX:MaxTenuringThreshold=10 
设置打印GC日志 -XX:+PrintGC。

    这里发现,不稳定参数的赋值语法还有点复杂,这里细分了几种类型。

2.3.1 不稳定参数赋值语法

1、数字类型参数值

    -XX:=,用=号并赋值数字。

    比如,-XX:SurvivorRatio=8,这个8是JDK的默认值。标识年轻代中Eden区大小/两个Survivor区的大小。也就是在年轻代内存划分,默认比例是Eden:S1:S0 = 8:1:1。

2、布尔类型参数值

    是通过+\-号,来表示启用、禁用。

-XX:+

-XX:-

    比如,刚才这个-XX:+UseConcMarkSweepGC,表示启用CMS垃圾收集器。

3、字符串类型参数值

    通过=并设置字符串值。

-XX:=

    比如:-XX:HeapDumpPath=./log/dump/oom.log,设置内存溢出快照保存地址。

2.3.2 那如何查看全部JVM -XX开头的参数呢?

    之前是java -X查看非标准参数,现在是 java -XX?

(base) MacBook-Pro ~ % java -XX
Unrecognized option: -XX
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

报错了,不能如此类推!这里末尾还需要加+PrintFlagsFinal才可以。

java -XX:+PrintFlagsFinal

三、按区域功能划分

    之前我们的文章系列2《JVM内存区域怎么划分,分别有什么用》有详细说过内存区域划分,里面有堆内存(分年轻代和老年代)、MetaSpace元数据区、虚拟机栈、程序计数器、系统直接内存几个区。老话长谈JVM调优,主要就是对堆内存GC进行调优。所以有必要专门了解整理堆内存相关参数。

3.1 堆内存相关参数

    我们这里把虚拟机栈、堆内存,MetaSpace元数据区相关参数都统一当做堆内存参数。除了刚才说的非标准参数-Xms、-Xmx、-Xmn,还有:

-XX:NewRatio=n,设置年轻代和年老代的比值为n;
-Xss,设置每个线程堆栈大小;
-XX:MaxMetaspaceSize 设置元数据区大小。

3.2 垃圾回收器参数

    与GC垃圾回收器相关的参数,我们划为一类。比如:

-XX:CMSFullGCsBeforeCompaction,CMS回收器完成多少次FGC后进行内存压缩;
-XX:CMSInitiatingOccupancyFraction=92,老年代占比大于92%后开始CMS FGC处理
-XX:+UseParNewGC,使用ParNew回收器来为年轻代做YGC;
-XX:ParallelGCThreads,负责垃圾回收的并发线程数量。

3.3 GC日志、OOM相关参数

    GC日志控制、OOM相关后置处理参数,划为一类。比如:

-XX:GCLogFileSize=n,GC log 滚动大小;
-Xloggc:log/myapp_gc.log,发生gc后相关gc日志保存;
-XX:+HeapDumpOnOutOfMemoryError ,内存溢出时导出整个堆信息,让JVM遇到OOM异常时能输出堆内信息;
-XX:HeapDumpPath=./oomlog/dump/,内存异常堆数据导出地址。

四、大厂生产调优实践参数模板

    在一线头部大厂,通过长期生产实践技术经验积累,基础技术架构组或者相关架构师会整理分享一个jvm最佳实践模板,帮助团队成员根据不同的业务场景快速调整出一份适合自己系统服务的JVM参数。让核心服务首次上线、以及后续业务规模增长,不管监控是否发现性能问题、还是业务爆涨,都能提供长期稳定运行支撑。

    这里分享一份生产实战调优常用(也可以说是必用的核心参数)的一个模板,大家不可盲目照搬直接应用,可以先保存,具体还是要看业务量、业务场景来调整,后续分享实战案例,帮助大家分析选择调优自己的系统服务。

-Xms8888M -Xmx8888M -Xmn6666M -Xss1M -XX:MetaspaceSize=256M 
-XX:MaxMetaspaceSize=256M 
-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=10
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=2 
-XX:+CMSParallelInitialMarkEnabled
XX:+CMSScavengeBeforeRemark -XX:+PrintGCApplicationStoppedTime 
-XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/mycompany/appname/oom

    这个模板,有些参数是非常必要的,比如:

-XX:+DisableExplicitGC: 关闭System.gc(),程序代码里主动gc是非常糟糕的。
-XX:+CMSParallelInitialMarkEnabled:CMS的初始标记阶段是会Stop the world,导致系统停顿,
加了这个参数可以开启多线程并发进行标记,减少暂停时间。
-XX:CMSScavengeBeforeRemark:CMS的重新标记阶段也是会Stop the world,打开这个参数好处,
就是可以在【重新标记】之前先执行YGC,YGC会回收没有再被年轻代引用的对象。
这样重新标记阶段就可以少扫描一些对象,提高CMS重新标记阶段的性能。

   其他几个,大家看第四部分参数清单去找对应含义和重要性。现附上JVM参数表格。

五、JVM参数详解说明表格

    粗略统计了一下,JDK8 JVM全部参数加起来有将近800个,实际我们常用的以及调优参数大概100多个,这里我们重点列举核心常用的参数。

参数名

参数意义、用法说明

默认值

-Xms

设置初始 Java 堆大小

-Xmx

堆内存可以扩展到最大大小

-Xmn

堆内存里年轻代的大小

默认是8

-Xss

线程堆栈大小,单位是:k或K,表示KB;m或M表示MB;g或G表示GB;

-Xnoclassgc

禁用类的GC垃圾收集

-Xincgc

启用增量垃圾收集

-Xcheck:jni

对 JNI 函数执行其他检查

-Xbatch

禁用后台编译

-Xloggc

指定GC日志路径,-Xloggc:/data/gclog/mygc.log

-Duser.timezone

指定时区

-XX:MaxMetaspaceSize

元数据区最大大小,这个地方放.class类信息

-XX:MetaspaceSize

类元数据区的大小,Metaspace第一次超出该大小时触发垃圾回收。

-XX:ThreadStackSize

线程堆栈大小

默认值1024

-XX:NewRatio

年轻代与年老代的比值,默认是2:1

默认值2

-XX:MaxTenuringThreshold

年轻代对象进入老年代的年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。

默认是15

-XX:LargePageSizeInBytes

内存页大小

-XX:PretenureSizeThreshold

可以直接进入老年代的大对象大小

-XX+BackgroundCompilation

启用后台编译。默认启用。如果禁用,则-XX:-BackgroundCompilation。

XX:+DisableExplicitGC

禁用System.gc()

默认是禁用

-XX:SurvivorRatio

Eden区与Survivor区的大小比值

默认是8,E:S1:S0=8:1:1

-XX: +PrintGCApplicationStoppedTime

打印应用程序由于GC而产生的停顿时间

-XX:ConcGCThreads

并发GC的线程数

-XX:ParallelGCThreads

负责垃圾回收的并发线程数量

-XX:+AggressiveHeap

启用Java堆优化

默认禁用

-XX:CMSInitiatingOccupancyFraction

老年代占比大于该参数比例后开始CMS FGC处理(这个值大于等于0则直接取百分号,小于0则根据公式来计算。往往需要配合-XX:+UseCMSInitiatingOccupancyOnly使用)

默认92

-XX:+CMSScavengeBeforeRemark

打开这个参数,CMS在【重新标记】之前先执行YGC

-XX: +UseCMSInitiatingOccupancyOnly

打开这个参数后,CMS通过CMSInitiatingOccupancyFraction的值进行垃圾回收。

-XX:+UseParNewGC

使用ParNew垃圾收集器

-XX:+UseConcMarkSweepGC

指定使用CMS垃圾回收器

-XX:+CMSClassUnloadingEnabled

CMS垃圾收集器时启用类卸载

默认启用

XX:CMSFullGCsBeforeCompaction

多少次GC后,CMS进行内存碎片整理

-XX:+PrintGCDetails

打印GC详细信息

-XX:+PrintHeapAtGC

打印GC前后详细的堆栈信息

-XX:+PrintReferenceGC

跟踪系统内的软引用,弱引用,虚引用和Finallize队列

-XX:OnOutOfMemoryError=”“

指定这个参数,可以在服务OOM后,执行自定义脚本,比如用来发送邮件告警信息,重启服务。="/opt/local/bin/alert-restart.sh"

-XX:HeapDumpPath =

OOM抛出异常时,将堆数据转储到HeapDumpPath目录

-XX:+PrintGC

简单打印GC日志

-XX:+UseSerialGC

使用串行垃圾收集器

-XX:+UseG1GC

使用G1垃圾回收器

-XX:MaxGCPauseMillis

设置并行收集最大暂停时间

-XX:+ExplicitGCInvokesConcurrent

允许使用System.gc()请求调用并发GC

-XX:+UseGCOverheadLimit

限制OOM异常之前JVM在GC上花费的时间比例

默认启用

.............

推荐阅读:

1、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

2、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

3、JVM进阶调优系列(1)类加载器原理一文讲透

4、JAVA并发编程系列(13)Future、FutureTask异步小王子

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
33 2
|
8天前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
25 3
|
8天前
|
Java Android开发 开发者
【编程进阶知识】精细调控:掌握Eclipse JVM参数配置的艺术
本文详细介绍了如何在Eclipse中配置JVM参数,包括内存的初始和最大值设置。通过具体步骤和截图演示,帮助开发者掌握JVM参数的精细调控,以适应不同的开发和测试需求。
26 1
|
5天前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。
|
8天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
23 4
|
2月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
8天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
29 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
11天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
|
8天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
24 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
11天前
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。