手把手教你一次GC调优!
共 2096字,需浏览 5分钟
·
2021-12-22 20:50
你知道的越多,不知道的就越多,业余的像一棵小草!
你来,我们一起精进!你不来,我和你的竞争对手一起精进!
编辑:业余草
chenxiao.blog.csdn.net
推荐:https://www.xttblog.com/?p=5300
JVM参数调优
一、调优基本概念
在调整性能时,JVM 有三个组件。
堆大小调整 垃圾收集器调整 JIT 编译器调整
大多数调优选项都与调整堆大小和选择的垃圾收集器有关。
同样,JIT 编译器对性能也有很大影响,但是这个对程序员自身要求非常高。
通常,在调优 Java 应用程序时,重点是以下两个主要目标之一:
响应性:应用程序或系统对请求的数据进行响应的速度,对于专注于响应性的应用程序,长的暂停时间是不可接受的,重点是在短时间内做出回应。 吞吐量:侧重于在特定时间段内最大化应用程序的工作量,对于专注于吞吐量的应用程序,高暂停时间是可接受的。
一般而言,系统瓶颈核心还是在应用代码,一般情况下无需过多调优,而且 JVM 本身在不断优化的过程。
二、常用 JVM 参数
文章围绕 Java8,下面的一些参数,在 JDK9 之后就被淘汰了,加红进行了凸显。
参数 | 说明 |
---|---|
-XX:+AlwaysPreTouch | JVM启动时分配内存,非使用时再分配 |
-XX:ErrorFile= filename | 崩溃日志 |
-XX:+TraceClassLoading | 跟踪类加载信息 |
-XX:+PrintClassHistogram | 按下Ctr+ Break后,打印类的信息 |
-Xmx -Xms | 最大堆和最小堆 |
-xx:permSize, -xx:metaspaceSize | 永久代/元数据空间 |
-XX:+HeapDumpOnOutOfMemoryError | 0OM时导出堆到文件 |
-XX:+HeapDumpPath | OOM时堆导出的路径 |
-XX:+OnOutOfMemoryError | JVM启动时分配内存,非使用时再分配在OOM时,执行一个脚本 |
java-XX:+PrintFlagsFinal -version 打印所有的-XX参数和默认值
「通用GC参数」
参数 | 说明 |
---|---|
-XX:ParallelGCThread | 并行GC线程数量 |
-XX:ConcGCThreads | 并发GC线程数量 |
-XX:MaxGCPauseMillis | 最大停顿时间,单位毫秒。GC尽力保证回收时间不超过设定值 |
-XX:GCTimeRatio | 0-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.class, args);
// 每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();
}, 1000, 333, TimeUnit.MILLISECONDS);
}
}
在运行之前要记得指定一下堆的大小
java -Xmx1024m -jar xxx.jar
启动是正常的。
上面已经能看到端口号了,但是我们这里用 jcmd 象征的看一下。
我们看一下堆的使用情况。
里面的数据都是动态的。
我们看一下调优的思路。
分析场景。例如:启动速度慢;偶尔出现响应慢于平均水平或者出现卡顿 确定目标。内存占用、低延时、吞吐量 收集日志。通过参数配置收集GC日志;通过JDK工具查看GC状态(例如jstat 实时查看) 分析日志。使用工具辅助分析日志,查看GC次数,GC时间(事后分析) 调整参数。切换垃圾收集器或者调整垃圾收集器参数
前两点的话,要和具体场景结合起来。文章主要展示整个过程该怎么走(相当于工具说明书),具体问题要具体分析。
java -Xmx1024m -Xloggc:/opt/gc.log -jar xxx.jar
我们这里添加了日志的收集。
我们去对应目录找一下日志,里面的参数我们都介绍过,这个不难看懂。
但是文件内容是非常非常多的,这样一点点的看可能有点困难,我们一边把文件下载下来,用 GCViewer 这个开源的工具来做这件事情。
GCViewer 工具,辅助分析 GC 日志文件https://github.com/chewiebug/GCViewer
。
打开下载到本地的gc.log文件。
再看右下角的信息,主要是一些汇总信息。各种数据的分析,比如平均,最大,最小等。
至于首页的图形,怎么看,可以直接参考文档:https://github.com/chewiebug/GCViewer
。
红线意思是什么等等,都写的很详细。
下面看一下 jstat 动态监控 GC 统计信息,采用的格式是间隔 1000 毫秒统计一次,每 10 行数据后输出列标题。
jstat -gc -h10 $(jcmd | grep "demo-0.0.1-SNAPSHOT.jar" | awk '{print $1}') 1000
里面的参数,我们说过了,可以参考我的历史文章:
下面看一下切换垃圾收集器或者调整垃圾收集器参数相关信息:
「垃圾收集器 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秒)。
「运行时JIT编译器优化参数」
JIT编译指的是字节码编译为本地代码(汇编)执行,只有热点代码才会编译为本地代码。解释器执行节约内存,反之可以使用编译执行来提升效率。
最后由于版本不断更新,JVM 参数和具体说明,建议需要时参考 oracle 官网的手册。
说实话,如果没有一个具体的线上场景,很难去演示调优的过程,这里能做到的就是,给出调优的思路、相关工具的使用,以及调优用到的参数信息。
参数的调整多少都会对 JVM 的运行有影响,比如 Parallel 参数调优中-XX:ParallelGCThreads
设置线程的数量,这个在不同配置的服务器上测试得到的结果会有差别,你可以在自己服务器上试试看。