这段时间接手项目出现各种问题,令人不胜烦扰。吐槽下公司做项目完全靠人堆,大上快上风格注定留下一地鸡毛,修修补补不如想如何提升同事代码水准免得背锅。偶然看到关于JMH对于优化java代码的直观性,于是有了这篇文章,希望能帮到大家。
一:基础介绍文章
基准测试神器JMH——详解36个官方例子 - 知乎
这里介绍引入项目方式,各种基本注解用法,用例。比较全面,推荐大家学习
二:boot项目实用
注意事项:1.jdk版本大于9,最好使用的openJDK,其他的也行
2.做测试最好放在test包下,不影响正常项目使用
2-1 一般项目优化需求:
一个简单方法,一个service接口(这个比较多。下面以这个为例)
场景1: 有个根据实例获取报警信息的接口
需求是要测试下,不同的多个实例对接口相应的影响,这里我分成1.2.3
直接test文件下新建
import com.chitic.things.device.api.request.monitor.AlarmSnsRequest; import com.chitic.things.device.service.MonitorOnlineService; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; /** * 类功能说明: // * * @author Zhouxl * @date 2023/8/10 14:25 */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Benchmark) @Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) //仿JVM进行预热行为,提高命中率(1s 3次) public class BenchmarkTest { private ConfigurableApplicationContext context; private MonitorOnlineService onlineService; @Setup public void init() { // 这里的WebApplication.class是项目里的spring boot启动类 context = SpringApplication.run(CommonApplication.class); // 获取需要测试的bean this.onlineService = context.getBean(MonitorOnlineService.class); } @Benchmark public void one(){ AlarmSnsRequest snsRequest =new AlarmSnsRequest(); String str="test"; snsRequest.setSns(Arrays.asList(str.split(","))); //测试接口方法 onlineService.alarmPageBySns(snsRequest); } @Benchmark public void two(){ AlarmSnsRequest snsRequest =new AlarmSnsRequest(); String str="test,test1"; snsRequest.setSns(Arrays.asList(str.split(","))); //测试接口方法 onlineService.alarmPageBySns(snsRequest); } @Benchmark public void three(){ AlarmSnsRequest snsRequest =new AlarmSnsRequest(); String str="test,test1,test2"; snsRequest.setSns(Arrays.asList(str.split(","))); //测试接口方法 onlineService.alarmPageBySns(snsRequest); } @TearDown public void down() { context.close(); } public static void main(String[] args)throws RunnerException { Options opts = new OptionsBuilder() .include(BenchmarkTest.class.getSimpleName()) .resultFormat(ResultFormatType.JSON) //导出json 可通过 http://deepoove.com/jmh-visual-chart/ 对比 //.addProfiler(StackProfiler.class) //栈内存解析,主要分析方法的耗时情况 // .addProfiler(GCProfiler.class) //JVM GC的情况 各区的情况(G1的垃圾回收器情况) //.addProfiler(HotspotMemoryProfiler.class) //使用HotSpot VM 进行内存占用分析 .forks(1) //进程数 .build(); new Runner(opts).run(); } }
测试结果:1个时耗时大概0.38s,2个和3个消耗会增加,但区别不大
可以在项目中对任意接口进行测试,还可以图形化接口比较,直接体现你的优化价值
JMH Visual Chart 生成的json引入这里生成图标
2-2 接口分析需求
场景2: 有个接口一直导致卡死或者内存OOM,代码是依托答辩,看了头晕
这里就需要借助JVM对堆栈内存,找出耗时情况,哪种应对哪个情况我已经上面列出来了:
package com.chitic.demo.controller.demo;
import com.chitic.demo.Application;
import com.chitic.demo.controller.HartCheck;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.profile.HotspotMemoryProfiler;
import org.openjdk.jmh.profile.StackProfiler;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* 类功能说明: //对堆栈和GC内部
*
* @author Zhouxl
* @date 2023/8/10 14:59
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) //仿JVM进行预热行为,提高命中率(1s 3次)
public class JvmTest {
private ConfigurableApplicationContext context;
private HartCheck hartCheck;
@Setup
public void init() {
// 这里的WebApplication.class是项目里的spring boot启动类
context = SpringApplication.run(Application.class);
// 获取需要测试的bean
this.hartCheck = context.getBean(HartCheck.class);
}
@Benchmark
public void one(){
//测试接口方法
hartCheck.getUser();
}
@TearDown
public void down() {
context.close();
}
public static void main(String[] args)throws RunnerException {
Options opts = new OptionsBuilder()
.include(JvmTest.class.getSimpleName())
.addProfiler(StackProfiler.class) //栈内存解析,主要分析方法的耗时情况
// .addProfiler(GCProfiler.class) //JVM GC的情况 各区的情况(G1的垃圾回收器情况)
// .addProfiler(HotspotMemoryProfiler.class) //使用HotSpot VM 进行内存分析
.resultFormat(ResultFormatType.JSON) //导出json 可通过 http://deepoove.com/jmh-visual-chart/ 对比
.forks(1) //进程数
.build();
new Runner(opts).run();
}
}
2-2-1.addProfiler(StackProfiler.class) //栈内存解析,主要分析方法的耗时情况
栈内存分析,主要看这些内容:
就可以看到栈内耗时情况了,然后动手处理
2-2-2.addProfiler(GCProfiler.class) //JVM GC的情况 各区的情况(G1的垃圾回收器情况)
G1垃圾回收器的各种状态情况
2-2-3..addProfiler(HotspotMemoryProfiler.class) //使用HotSpot VM 进行内存分析
展示数据和jstack的类似,能快速定位到哪些类占用内存,因为我的项目用的是graalvm17没办法用,只能各位自行探索了