手把手教你一次GC调优!

业余草

共 2096字,需浏览 5分钟

 ·

2021-12-22 20:50

你知道的越多,不知道的就越多,业余的像一棵小草!

你来,我们一起精进!你不来,我和你的竞争对手一起精进!

编辑:业余草

chenxiao.blog.csdn.net

推荐:https://www.xttblog.com/?p=5300

JVM参数调优

一、调优基本概念

在调整性能时,JVM 有三个组件。

  1. 堆大小调整
  2. 垃圾收集器调整
  3. JIT 编译器调整

大多数调优选项都与调整堆大小和选择的垃圾收集器有关。

同样,JIT 编译器对性能也有很大影响,但是这个对程序员自身要求非常高。

通常,在调优 Java 应用程序时,重点是以下两个主要目标之一:

  • 响应性:应用程序或系统对请求的数据进行响应的速度,对于专注于响应性的应用程序,长的暂停时间是不可接受的,重点是在短时间内做出回应。
  • 吞吐量:侧重于在特定时间段内最大化应用程序的工作量,对于专注于吞吐量的应用程序,高暂停时间是可接受的。

一般而言,系统瓶颈核心还是在应用代码,一般情况下无需过多调优,而且 JVM 本身在不断优化的过程。

二、常用 JVM 参数

文章围绕 Java8,下面的一些参数,在 JDK9 之后就被淘汰了,加红进行了凸显。

参数说明
-XX:+AlwaysPreTouchJVM启动时分配内存,非使用时再分配
-XX:ErrorFile= filename崩溃日志
-XX:+TraceClassLoading跟踪类加载信息
-XX:+PrintClassHistogram按下Ctr+ Break后,打印类的信息
-Xmx -Xms最大堆和最小堆
-xx:permSize, -xx:metaspaceSize永久代/元数据空间
-XX:+HeapDumpOnOutOfMemoryError0OM时导出堆到文件
-XX:+HeapDumpPathOOM时堆导出的路径
-XX:+OnOutOfMemoryErrorJVM启动时分配内存,非使用时再分配在OOM时,执行一个脚本
java-XX:+PrintFlagsFinal -version  打印所有的-XX参数和默认值

「通用GC参数」

参数说明
-XX:ParallelGCThread并行GC线程数量
-XX:ConcGCThreads并发GC线程数量
-XX:MaxGCPauseMillis最大停顿时间,单位毫秒。GC尽力保证回收时间不超过设定值
-XX:GCTimeRatio0-100的取值范围,表示垃圾收集时间占总时间的比,默认99,即最大允许1%时间进行GC
-XX. SurvivorRatio设置Eden区大小和 Survivor区大小的比例, 8表示两个 Survivor:Eden=2:8,即一个 Survivor占年轻代的1/10
-XX:NewRatio新生代和老年代的比,4表示新生代老年代=1:4,即年轻代占堆的1/5
-verbose:gc, -XX;+printGC打印GC的简要信息
-XX:+PrintGCDetails打印GC详细信息
-XX:+PrintGCTimeStamps打印CG发生的时间戳
-Xloggc:log/gc.log指定 GC log的位置,以文件输出
-XX:ParallelGCThread每次一次GC后,都打印堆信息

三、GC调优思路

「首先准备环境」

创建一个springboot的空项目,在启动类部分添加一下代码,打包,放在阿里云服务器上运行。

测试环境:JVM配置为1核2G,JAVA8,固定设置堆大小 1G

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.classargs);
        // 每333毫秒创建150线程,每个线程创建一个512kb的对象,最多一秒同时存在450线程,占用内存225m,查看6C的情况
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
            new Thread(() -> {
                for (int i = 0; i < 150; i++) {
                    try {
                        //  不干活,专门创建512kb的小对象
                        byte[] temp = new byte[1024 * 512];
                        Thread.sleep(new Random().nextInt(100)); // 随机睡眠200毫秒秒以内
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }, 1000333, TimeUnit.MILLISECONDS);
    }
}

在运行之前要记得指定一下堆的大小

java -Xmx1024m -jar xxx.jar

启动是正常的。

SpringBoot项目启动

上面已经能看到端口号了,但是我们这里用 jcmd 象征的看一下。

jcmd命令

我们看一下堆的使用情况。

jmap堆

里面的数据都是动态的。

我们看一下调优的思路。

  1. 分析场景。例如:启动速度慢;偶尔出现响应慢于平均水平或者出现卡顿
  2. 确定目标。内存占用、低延时、吞吐量
  3. 收集日志。通过参数配置收集GC日志;通过JDK工具查看GC状态(例如jstat 实时查看)
  4. 分析日志。使用工具辅助分析日志,查看GC次数,GC时间(事后分析)
  5. 调整参数。切换垃圾收集器或者调整垃圾收集器参数

前两点的话,要和具体场景结合起来。文章主要展示整个过程该怎么走(相当于工具说明书),具体问题要具体分析。

java -Xmx1024m -Xloggc:/opt/gc.log -jar xxx.jar

我们这里添加了日志的收集。

配置GC日志

我们去对应目录找一下日志,里面的参数我们都介绍过,这个不难看懂。

gc日志内容

但是文件内容是非常非常多的,这样一点点的看可能有点困难,我们一边把文件下载下来,用 GCViewer 这个开源的工具来做这件事情。

GCViewer 工具,辅助分析 GC 日志文件https://github.com/chewiebug/GCViewer

GCViewer

打开下载到本地的gc.log文件。

gc.log

再看右下角的信息,主要是一些汇总信息。各种数据的分析,比如平均,最大,最小等。

GC日志信息汇总
GC内存汇总信息
GC暂停

至于首页的图形,怎么看,可以直接参考文档:https://github.com/chewiebug/GCViewer

红线意思是什么等等,都写的很详细。

GC饼图信息

下面看一下 jstat 动态监控 GC 统计信息,采用的格式是间隔 1000 毫秒统计一次,每 10 行数据后输出列标题。

jstat -gc -h10 $(jcmd | grep "demo-0.0.1-SNAPSHOT.jar" | awk '{print $1}'1000

里面的参数,我们说过了,可以参考我的历史文章:

jstat 动态监控 GC 统计信息

下面看一下切换垃圾收集器或者调整垃圾收集器参数相关信息:

「垃圾收集器 Parallel参数调优」

这是JDK默认的收集器–吞吐量优先

参数说明
-XX:+UseParallelGC新生代使用并行回收收集器
-XX:+Use ParalleloldGC老年代使用并行回收收集器
-XX:ParallelGCThreads设置用于垃圾回收的线程数
-XX:+UseAdaptiveSizePolicy打开自适应GC策略

自适应GC策略是指自动调整分代分区的大小。UseAdaptiveSizePolicy自适应默认开启。

「垃圾收集器CMS参数调优」

关于CMS

  • 响应时间优先2
  • Parallel gc无法满足应用程序延迟要求时再考虑使用CMS垃圾收集器。
  • 新版建议用G1垃圾收集器


「垃圾收集器G1参数调优」

关于G1:

  • 兼顾吞吐量和响应时间
  • 超过50%的Java堆被实时数据占用。
  • 建议大堆(大小约为6GB或更大)
  • 且GC延迟要求有限的应用(稳定且可预测的暂停时间低于0.5秒)。
垃圾收集器G1参数调优

「运行时JIT编译器优化参数」

JIT编译指的是字节码编译为本地代码(汇编)执行,只有热点代码才会编译为本地代码。解释器执行节约内存,反之可以使用编译执行来提升效率。

运行时JIT编译器优化参数

最后由于版本不断更新,JVM 参数和具体说明,建议需要时参考 oracle 官网的手册。

说实话,如果没有一个具体的线上场景,很难去演示调优的过程,这里能做到的就是,给出调优的思路、相关工具的使用,以及调优用到的参数信息。

参数的调整多少都会对 JVM 的运行有影响,比如 Parallel 参数调优中-XX:ParallelGCThreads设置线程的数量,这个在不同配置的服务器上测试得到的结果会有差别,你可以在自己服务器上试试看。

浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报