JVM 相关的内容其实我在刚学 Java 的时候就学了一些,但当时觉得这些东西新版本的 JVM 完全都可以不用考虑了,而且事实上我也没真的这么深度参与过一个需要我来考虑这方面的项目,所以就三倍速快进掉了这部分,导致知识根本没进大脑。
最近精弘退休老干部们和现役部长团讨论了下,决定一点点迁移技术栈到 Java 上来,面对一个需要从零开始的论坛,我这一把老骨头也得再发发光了,重新学了这部分知识,做个简单小记方便后续回顾。
概述 链接到标题
JVM 肯定免不了要和调优联系起来,而开发过程中 99% 的性能问题是我们开发者的代码出了问题,例如循环调用 rpc 和数据库了,对象没有释放了,而不是 JVM 参数设置的有问题,所以这部分东西应该是我们把代码看了又看,分析了又分析,觉得优化不了才拿出来思考的,但愿它们这辈子都派不上用场吧。
还有就是在没有全面监控和收集性能数据之前,调优就是一句屁话,没有数据做支撑,调优根本无从调起,照抄网上给的参数只会事倍功半,因此建立一个全面监控反而是一个项目起步只是需要更加重视的。
最后如果真的需要进行 JVM 调优了,请从以下三个方面考虑:
- 最大堆和最小堆大小
- GC 收集器的选择
- 新生代(年轻代)的大小
JVM 选项规则 链接到标题
标准选项 链接到标题
java -version
这些选项在任何版本的 JVM,任何平台之下都可以使用。
非标准选项 链接到标题
java -Xms10m
这个虽然说是非标准的,部分版本的 JVM 可能会识别不了,但事实上现在市面上绝大多数 JVM 版本和平台都适配了这批选项,基本上可以大胆放心的使用了,真的遇到了问题再 case by case 的查就好。
不稳定参数 链接到标题
java -XX:+PrintGCDetails
虽然这种参数使用频率比较高,但这种参数就在不同 JVM 下有更高概率存在差异了,并且随时可能会被移除,所以需要小心使用,用之前最好查一下选择的 JVM 版本是否适配。
- 表示开启,- 表示关闭
JVM 优化选项 链接到标题
虽说照抄是不好的,但为了后续记录方便,我下面还是提供了一个 JVM 启动参数的模板,后续真要调优的时候改吧改吧就差不多能应付。
java -jar -XX:+UseG1GC -Xms2G -Xmx2G -Xss256k -XX:MaxGCPauseMillis=300 -Xloggc:/logs/gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails test.jar
GC 收集器的选择 链接到标题
jdk 1.8 之后可以无脑选择 G1 收集器,1.9 后默认就是它,更智能性能也更好,简直找不出来不选它的理由。
以下内容是千问帮我总结的 GC 收集器介绍和优缺点。
1. Serial GC 链接到标题
- 算法: 标记-复制(Mark-Copy)
- 优点:
- 简单且高效,适合于小型应用或单线程环境。
- 对于小堆大小的应用程序,性能非常好。
- 缺点:
- 在执行GC时会暂停所有应用程序线程(Stop-the-world事件),不适合服务器端应用。
2. Parallel GC (Throughput Collector) 链接到标题
- 算法: 标记-清除-压缩(Mark-Sweep-Compact),多线程并行处理。
- 优点:
- 使用多个线程进行垃圾回收,提高了吞吐量。
- 对于大堆和多核处理器优化良好。
- 缺点:
- 依然会有较长时间的停顿,特别是在Full GC期间。
3. CMS (Concurrent Mark Sweep) GC 链接到标题
- 算法: 主要使用标记-清除(Mark-Sweep)算法,并发执行大部分操作。
- 优点:
- 目标是减少停顿时间,特别适合需要低延迟的应用。
- 大部分工作与应用程序并发运行,减少了GC对应用的影响。
- 缺点:
- 不执行压缩,可能导致内存碎片化问题。
- CMS在高负载下可能会出现“Concurrent Mode Failure”,导致更长的停顿。
4. G1 (Garbage First) GC 链接到标题
- 算法: 标记-复制(Mark-Copy),区域化(Region-based)收集策略。
- 优点:
- 可以设置最大GC停顿时间目标,更加可预测的停顿时间。
- 针对大堆进行了优化,同时保持较低的停顿时间。
- 减少了内存碎片问题。
- 缺点:
- 比CMS稍微复杂一些,可能需要更多的调优。
- 在某些情况下,其吞吐量可能不如Parallel GC。
5. ZGC (Z Garbage Collector) 链接到标题
- 算法: 基于染色指针技术的并发标记-复制(Concurrent Mark-Copy)。
- 优点:
- 设计目标为非常低的停顿时间(小于10ms),几乎不影响应用程序性能。
- 支持非常大的堆(TB级别)。
- 缺点:
- 截至目前,它还在实验阶段,可能不完全稳定或适用于所有场景。
- 吞吐量可能低于G1或Parallel GC。
6. Shenandoah GC 链接到标题
- 算法: 并发标记-整理(Concurrent Mark-Compact)。
- 优点:
- 类似ZGC,旨在提供低停顿时间(通常不超过10ms),但支持更广泛的JVM版本。
- 通过减少停顿时间来提高响应性。
- 缺点:
- 同样处于实验性阶段,可能不稳定。
- 吞吐量可能低于其他非低延迟收集器。
最大堆和最小堆的大小 链接到标题
首先 -Xms
和 -Xmx
要设置成一样大,这样可以减少内存的交换,避免不必要的性能浪费。
然后这个大小呢要根据实际情况来定,一般是第一次启动的时候设置的大一些,然后跟踪监控日志,观察一段时间 GC 时堆的占用大小,最终调整为堆巅峰大小的两到三倍即可。
-Xloggc
可以设置 GC log 的存储路径,-XX:+PrintGCTimeStamps
和 -XX:+PrintGCDetails
一起使用可以打印详细的 GC 信息和每次 GC 发生的时间戳。
GC 收集器的最大停顿时间 链接到标题
这个值一般在 200~500 毫秒之间,增大这个值可以减少 GC 的次数,提高吞吐量,但过大会影响用户体验。
每个线程的栈大小 链接到标题
一般栈空间 128K 就够用了,如果业务代码比较复杂,128K 会爆,可以选择 256K,但超过 256K 就需要考虑优化代码而不是再去上调栈空间了。
新生代的大小 链接到标题
G1 收集器一般不设置新生代的大小,这个值是会动态调整的。