1、使用GC调优的目的
从性能的角度看,通常关注三个方面
内存占用(footprint)、延时(latency)和吞吐量(throughput)
大多数情况下调优会侧重于其中一个或者两个方面的目标,很少有情况可以兼顾三个不同的角度。
当然,除了上面通常的三个方面,也可能需要考虑其他 GC 相关的场景,例如,OOM 也可能与不合理的 GC 相关参数有关;或者,应用启动速度方面的需求,GC 也会是个考虑的方面。
2、GC事件的分类
根据垃圾收集回收的区域不同,垃圾收集主要通常分为Young GC、Old GC、Full GC、Mixed GC
3、常见的 GC 调优思路
3.1降低 Minor GC 频率
通常情况下,由于新生代空间较小,Eden 区很快被填满,就会导致频繁 Minor GC,因此我们可以通过以下方法来降低 Minor GC 的频率。
-
调整堆大小:通过增大年轻代的大小,你可以减少Minor GC的频率。你可以使用-Xmx和-Xms参数来增加堆的大小,例如:-Xmx2g-Xms2g表示将堆的最大和初始大小设置为2GB。
-
.检查对象的生命周期:如果你的应用程序创建了大量的临时对象,你可以考虑优化它们的使用方式。尽量重用对象,避免频繁地创建和销毁对象。
-
检查内存泄漏:频繁的Minor GC也可能是由于内存泄漏导致的。确保你的代码中没有以外的对象引用导致无法回收。
-
调整垃圾收集器参数:Java提供了不同的垃圾收集器和参数进行调整。你可以尝试不同的收集器(如Parallel、CMS、G1等)和调整相关参数,以找到最适合你的应用程序配置。
-
减少对象分配:减少对象分配可以降低Minor GC的频率。尽量使用对象池或缓存来重用对象,减少对象的创建和销毁。
-
监控和分析:使用工具进行监控和分析,例如JDK自带的VisualVM或者其他的性能分析工具,可以帮助你了解应用程序的内存使用情况,定位瓶颈和优化点。
3.2、降低 Full GC 的频率
通常情况下,由于堆内存空间不足或老年代对象太多,会触发 Full GC,频繁的 Full GC 会带来上下文切换,增加系统的性能开销。因此我们可以通过以下方法来降低 Full GC 的频率。
FullGC触发条件:
老年代空间不足
每次晋升到老年代的对象平均大小 > 老年代剩余空间
MinorGC后存活的对象超过了老年代剩余空间
优化策略
调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组
减少创建大对象:在平常的业务场景中,我们习惯一次性从数据库中查询出一个大对象用于 web 端显示。一次性查询出 60 个字段的业务操作,这种大对象如果超过年轻代最大对象阈值,会被直接创建在老年代;即使被创建在了年轻代,由于年轻代的内存空间有限,通过 Minor GC 之后也会进入到老年代。 这种大对象很容易产生较多的 FullGC。我们可以将这种大对象拆解出来,首次只查询一些比较重要的字段,如果还需要其它字段辅助查看,再通过第二次查询显示剩余的字段。
4、常用jvm调优参数
参数 | 含义 |
---|---|
-Xms | 用于设置JVM初始分配的堆内存大小,即Java虚拟机启动时所占用的内存大小 |
-Xmx或–XX:MaxHeapSize=size | 设置最大 Java 堆大小,最大不应该超过总内存的70% |
-Xmn或(–XX:NewSize=size+ -XX:MaxNewSize=size) | 设置新生代大小。整个堆大小=新生代大小 + 年老代大小 + 常量池Sun官方推荐配置为整个堆的3/8 |
–XX:InitialSurvivorRatio=ratio和–XX:+UseAdaptiveSizePolicy | 根据应用使用情况,自动调整幸存区的比例。可以根据年轻代(Y)大小和初始幸存区比例®来计算幸存区初始空间(S)大小,公式:S=Y/(R+2)。公式中的 2 代表有2个幸存区,幸存区初始比例®越大,则幸存区初始空间越小。默认值 8。如果年轻代空间是 2MB,幸存区空间则为 0.2 MB。 |
-XX:SurvivorRatio=ratio | 设置 eden 区和幸存区 survivor 区比率。默认值 8。 |
-XX:MaxTenuringThreshold=threshold | 即年轻代晋升老年代的最大年龄阈值。默认值是15 |
-XX:+PrintTenuringDistribution | 打印晋升年龄信息。默认禁用此选项。 |
-XX:+PrintGCDetails -verbose:gc | 用于打印输出详细的GC收集日志的信息 |
-XX:+ScavengeBeforeFullGC | 新生代GC优先于Full GC执行 |