Springboot中,我们可以使用监控工具Actuator,查看和变更spring的状态,但是Actuator是有可能引起内存溢出的问题的,具体原因,分析如下:
一、Filter
在Actuator中,有一个过滤器,即MetricsWebFilter,请求监控过滤器,其filter方法如下:
private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) {
long start = System.nanoTime();
return call.doOnSuccess((done) -> success(exchange, start))
.doOnError((cause) -> error(exchange, start, cause));
}
当请求完成后,无论是成功或者是失败,其都会做相应的处理,成功:
private void success(ServerWebExchange exchange, long start) {
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, null);
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
TimeUnit.NANOSECONDS);
}
失败:
private void error(ServerWebExchange exchange, long start, Throwable cause) {
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause);
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
TimeUnit.NANOSECONDS);
}
在success和error方法的第一行,创建了一个tags,tags是用method,uri,status等组成的一个集合。
2、Meter.Id
后面再Timer类的register方法中,tags成了Meter.Id中的一个属性,被后续使用:
public Timer register(MeterRegistry registry) {
// the base unit for a timer will be determined by the monitoring system implementation
return registry.timer(new Meter.Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(),
pauseDetector == null ? registry.config().pauseDetector() : pauseDetector);
}
在后面获取Meter的时候,会查看是否已经在内存中缓存了Meter,如果存在就返回相应的Meter,如果不存在就创建Meter
private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
BiFunction<Id, /*Nullable Generic*/ DistributionStatisticConfig, Meter> builder,
Id mappedId, Function<Meter.Id, ? extends Meter> noopBuilder) {
Meter m = meterMap.get(mappedId);
if (m == null) {
if (isClosed()) {
return noopBuilder.apply(mappedId);
}
synchronized (meterMapLock) {
m = meterMap.get(mappedId);
if (m == null) {
m = builder.apply(mappedId, config);
此时我们看下Meter.Id的equals方法,看下是怎么判断数据是否存在的,如下
return Objects.equals(name, meterId.name) && Objects.equals(tags, meterId.tags);
可以看到equals方法中,判断了name和tags都相等,才算存在。
三、内存溢出
在LatencyStats类中,有许多的对象,占用了部分内存空间:
private volatile AtomicHistogram activeRecordingHistogram;
private Histogram activePauseCorrectionsHistogram;
private AtomicHistogram inactiveRawDataHistogram;
private Histogram inactivePauseCorrectionsHistogram;
因为每次参数变动都会生成一个LatencyStats,由于这些LatencyStats一直存在于内存中,请求次数多了,便会造成内存溢出。
四、解决办法
解决方法有两个,其官网介绍如下:
Micrometer ships with a simple, in-memory backend that is automatically used as a fallback if no other registry is configured. This allows you to see what metrics are collected in the metrics endpoint.
The in-memory backend disables itself as soon as you’re using any of the other available backend. You can also disable it explicitly:
官网地址:spring-boot:metrics
一个是禁用Metrics(Actuator)的统计在内存中,
management.metrics.export.simple.enabled=false
还有一个是使用别的registry。