网站首页 > 文章精选 正文
一、99.9%的人没有JVM调优经验
其实,JVM调优并不是每个开发者都会接触到的技能,原因很简单:JVM通常不需要调优!
如果你的 JVM 真的出现了严重的 GC 压力,第一步想到的应该是优化代码,而不是直接上手去调整 JVM 参数。因为调整 JVM 可能带来以下问题:
- 不可维护:调优后,可能下一个维护者难以理解这些修改。
- 不可拓展:假设当前流量已经优化好了,但未来流量大幅变化时,这些参数可能失效。
尽管如此,面试时常常会有面试官问起 JVM 调优。那么,他们真的在问 JVM 调优的经验吗?
二、面试官实际上想问什么?
他们更多的是在考察以下方面:
- 你对 JVM GC 的理解
- 你对不同 GC 回收器的熟悉度
- 你是否了解常见的 JVM GC 参数配置
接下来我们以 Java 17 中的G1垃圾回收器为例,来看一些常见的 GC 相关参数。
在 G1 垃圾回收器中,堆内存被划分为多个Region,虽然这些 Region 的大小是相同的,但是它们属于不同的代。了解这些原理有助于我们更好地优化 JVM 的 GC 性能。
内存区域划分:
- E(Eden)区:存放新创建的对象
- S(Survivor)区:存放从Eden区存活下来的对象
- O(Old)区:存放长期存活的对象
- H(Humongous)区:专门存放超大对象
大对象的判定标准: 当一个对象的大小超过单个 Region(内存区域) 大小的 50%(0.5个Region)时,它就被视为大对象(Humongous对象)
三、常见的垃圾回收器
目前常见的垃圾回收器包括下面 3 种:
- CMS (Concurrent Mark-Sweep): JDK 14之后不再支持
- G1 (Garbage First): JDK 8-17版本的默认垃圾回收器
- ZGC (Z Garbage Collector): 从JDK 21开始,成为默认的垃圾回收器。
在启动 JVM 时,可以通过指定参数来使用不同的垃圾回收器。
-XX:+UseZGC
-XX:+UseG1GC
-XX:+UseConcMarkSweepGC
四、常见的调优策略
1. 设置堆大小
一般来说,我们建议将 JVM 的堆内存大小进行固定,避免堆内存发生动态扩缩容。
# 例如固定为2GB
-Xms 2048M -XMx 2048M
固定堆内存的好处:
- 避免堆的扩缩容:堆内存的扩缩容会影响 GC 的时间,带来更多的停顿时间,尤其是 G1这 种需要预测停顿时间的垃圾回收器,会导致预测变得不准确。
- 减少内存碎片:在 G1 中,堆内存被划分为多个 Region,如果堆大小不固定,Region 的大小也会不固定,可能导致内存浪费。
2. 设置Region大小
G1 将堆内存划分为多个大小相等的 Region,Region 的大小一般是 2 的幂,从 1MB 到 32MB 不等。
合理调整Region大小能更好地配合应用的实际需求,提升性能表现,减少停顿时间,并且优化堆内存管理。
对于较小的堆内存,建议设置较小的 Region 大小,例如2MB或4MB。 对于较大的堆内存,建议设置较大的Region大小。
# 例如设置Region 为4M
-XX:G1HeapRegionSize=4M
3. 设置最大目标停顿时间
上面说过 G1 垃圾回收器的最大特点是停顿预测模型。默认的停顿时间为 200ms。
因此我们可以设置一个目标停顿时间:
# 设置目标停顿时间为 100 ms
-XX:MaxGCPauseMillis=100
注意: JVM 会尽量保证垃圾回收停顿时间不超过这个值,但不能完全保证。所以针对一些延迟敏感的场景,可以适当调小停顿时间。
4. 设置标记线程数和回收线程数
G1 在进行标记对象为垃圾和回收垃圾时,都是多线程操作的。 因此我们可以根据需要去调整线程数。
# 并行回收线程数(默认值是 CPU 核心数)
-XX:ParallelGCThreads=8
# 并发标记线程数
-XX:ConcGCThreads=4
说到最后,这些调优参数在哪使用呢? 其实是在我们运行服务时设置的,例如:
# -Xms4G:设置JVM堆内存的初始大小为4GB。这意味着JVM启动时会分配4GB的堆内存
# -Xmx4G:设置JVM堆内存的最大大小为4GB。JVM堆的大小不会超过4GB
# -XX:+UseG1GC:启用G1垃圾回收器
# -XX:MaxGCPauseMillis=200:设置G1垃圾回收器的最大目标停顿时间为200 毫秒
# -XX:G1HeapRegionSize=4M:设置G1垃圾回收器的Region大小为4MB
# -XX:InitiatingHeapOccupancyPercent=45:设置堆的占用比例为45%。当堆内存的占用达到该比例时,G1垃圾回收器会开始触发并发标记过程,准备进行垃圾回收
# -Xlog:gc*,gc+heap=debug:file=gc.log:启用GC日志记录,gc*表示记录所有GC日志,gc+heap=debug表示记录GC过程中堆内存的详细调试信息,并将日志输出到gc.log文件中。
java -Xms4G -Xmx4G \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=4M \
-XX:InitiatingHeapOccupancyPercent=45 \
-Xlog:gc*,gc+heap=debug:file=gc.log \
-jar my-app.jar
猜你喜欢
- 2025-07-23 JVM中哪些是线程共享区,哪些是线程独占区
- 2025-07-23 线上服务 FGC 问题排查,看这篇就够了
- 2025-07-23 JVM运行时内存区域、堆内存分段机制
- 2025-07-23 JVM运行数据区深度解析(jvm运行时区域)
- 2025-07-23 开发者必备!线上内存泄漏排查的 8 个 Linux 实战工具
- 2025-07-23 内存溢出满分排查步骤,看完不用担心拿不到Offer了
- 2025-07-23 关于JVM调优,看这篇就够了(jvm调优实战简书)
- 2025-07-23 那么大个对象的垃圾回收有什么不同
- 2025-07-23 GC分代年龄为什么是15?(对象gc的分代年龄保存在)
- 2025-07-23 JVM参数调优,一文让你搞懂,再也不为应用操心了!
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)
- fmt.println (52)