随着应用程序的复杂性和负载的不断增加,对JVM进行调优,也是保障系统稳定性的一个重要方向。
需要注意,调优并非首选方案,一般来说解决性能问题还是要从应用程序本身入手(业务日志,慢请求等),只有在必要时才对jvm进行调优(gc日志和监控异常)。
下面会提供一个线上案例,说明gc调优的重要性。
02_jvm性能调优_垃圾收集器的实现-CSDN博客
调优关注指标
吞吐量
程序运行过程中执行两种任务,执行业务代码的任务和执行垃圾回收的任务;
吞吐量计算公式:CPU在用户应用程序的运行时间/(CPU在用户应用程序的运行时间+CPU在垃圾回收程序的运行时间)
简单理解就是,吞吐量大,意味着应用程序运行的时间越多,执行业务的任务越多。
那么吞吐量多大合适呢,对于我们业务系统来说,肯定是越大越好了,但吞吐量太大会导致什么问题呢?
停顿时间
因为JVM在进行垃圾回收的时候,有些特有的阶段需要停止业务程序的运行,只有这样它才能准确的找到所有可达的节点,确定哪些是垃圾对象并进行清理。
例如:gc停顿100ms,意味着在这100ms内应用程序无法运行。注意这里的停顿时长,只是JVM停止业务程序执行垃圾回收任务的时长,并不是总的垃圾回收时间(有的收集器是一个回收阶段是有多个停顿的)
堆内存大小
当gc完成后,通常会释放一大块内存空间,所以如果频繁的gc,但可用内存没有下降的趋势(或趋势比较小),通常暗示有内存泄漏的问题。
以上三者的关系
这3个指标不可能同时达到,因为他们是一个不可能的关系。
内存变大,要回收的东西肯定变多,停顿时间肯定也会延长。
吞吐量增加,必然要减少垃圾回收的频率,频率降低,垃圾回收停顿的时间也会延长。
停顿时间减少,需要增加gc的频率,导致吞吐量下降。
因此,目前的调优方向主要是吞吐量和暂停时间。(我们的目标是停顿时间竟可能小的情况下,达到最大的吞吐量)
案例-停顿时间过高
系统配置2c12g,默认使用并行回收器。
通过下图的监控,可以发现,gc频率低,但停顿时间比较长(通常停顿时间尽量不要超过300ms)
因为行回收器会导致暂停时间变长,可以考虑更换垃圾收集器(G1) -XX:+UseG1GC -XX:MaxGCPauseMillis=300 ,通过设置合理的暂停时间,减低停顿时间。
为什么没有使用cms呢?
在 JDK 1.8 中,G1 垃圾收集器(Garbage-First Garbage Collector)被引入作为一种新的选择,旨在替代老旧的 CMS(Concurrent Mark Sweep)收集器。随着 Java 平台的发展,CMS 逐渐退出了舞台,原因主要包括以下几点:
1. **停止更新和维护**:
自 JDK 9 开始,CMS 收集器就被标记为不推荐使用(deprecated),并在后续的版本中被彻底移除。Oracle 宣布不再对 CMS 收集器进行更新和维护,这意味着任何新的性能改进或者 bug 修复都不会应用于 CMS。
2. **并发模式失败**:
CMS 收集器在处理大量并发数据时可能会遇到 "并发模式失败"(Concurrent Mode Failure)的问题。这种情况发生时,CMS 收集器无法在用户线程运行的同时完成垃圾回收,导致它不得不进行一次完全的垃圾回收(Full GC),这将暂停所有用户线程,影响应用程序的响应时间。
3. **内存碎片问题**:
CMS 是基于标记-清除算法的,这意味着它在回收内存时不会进行压缩,从而可能导致内存碎片。内存碎片会随着应用程序的运行逐渐累积,最终可能导致需要更大的连续内存空间时无法找到足够的空间,进而触发 Full GC。
4. **G1 收集器的优势**:
G1 收集器被设计为一种服务器端的垃圾收集器,适用于多核处理器和大内存的机器。它具有以下优点:
- **预测性停顿时间模型**:G1 收集器可以设置期望的停顿时间目标(Pause Time Goal),并尽可能地在这个时间内完成垃圾回收,从而提供更可控的停顿时间。
- **并行和并发回收**:G1 能够充分利用多核处理器,同时在并发阶段减少应用程序的停顿时间。
- **分区堆**:G1 将堆内存分割成多个区域(Region),这样可以更高效地进行垃圾回收,尤其是在处理大堆时。
- **增量式清理**:G1 收集器可以逐步清理堆内存,而不需要一次性清理全部空间,这有助于避免长时间的停顿。
- **压缩空间**:G1 在回收过程中可以进行空间压缩,从而减少内存碎片问题。
5. **社区和官方的推动**:
Oracle 和 Java 社区推动使用更现代的垃圾收集器,如 G1 和后来的 ZGC(Z Garbage Collector)以及 Shenandoah,这些收集器旨在提供更低的停顿时间和更好的性能,特别是在大内存和多核心的环境中。
由于上述原因,G1 收集器成为了 JDK 9 及以后版本的默认垃圾收集器,并且随着 Java 平台的发展,G1 和其他新的垃圾收集器(如 ZGC 和 Shenandoah)继续得到改进,以满足现代应用程序对性能和响应时间的要求。