Webclient 在 Docker 中泄漏内存?

Webclient Leaks Memory In Docker?

我正在尝试在我的项目中使用 Webclient,但是当我加载测试时,我注意到 docker 内存使用量在实例终止之前不会下降。

@Component
public class Controller {

  //This is an endpoint to another simple api
  //I use my local Ip instead of localhost in the container
  private static final String ENDPOINT = "http://localhost:9090/";

  private WebClient client;
  
  public Controller(WebClient.Builder client) {
    super();
    this.client = client.build();
  }

  @Bean
  public RouterFunction<ServerResponse> router() {

    return RouterFunctions.route(GET("helloworld"), this::handle);
  }

  Mono<ServerResponse> handle(ServerRequest request) {

    Mono<String> helloMono =
        client.get().uri(ENDPOINT + "/hello").retrieve().bodyToMono(String.class);

    Mono<String> worldMono =
        client.get().uri(ENDPOINT + "/world").retrieve().bodyToMono(String.class);

    return Mono.zip(helloMono, worldMono, (h, w) -> h + w)
        .flatMap(s -> ServerResponse.ok().bodyValue(s));
  }
}

这也是我的docker文件。

FROM openjdk:8

ENV SERVICE_NAME reactive-hello-world

ADD target/reactive-hello-world-*.jar $APP_HOME/reactive-hello-world.jar

RUN mkdir /opt/reactor-netty/


EXPOSE 9010

CMD java \
    -Dcom.sun.management.jmxremote=true \
    -Dcom.sun.management.jmxremote.local.only=false \
    -Dcom.sun.management.jmxremote.authenticate=false \
    -Dcom.sun.management.jmxremote.ssl=false \
    -Djava.rmi.server.hostname=localhost \
    -Dcom.sun.management.jmxremote.port=9010 \
    -Dcom.sun.management.jmxremote.rmi.port=9010 \ 
    -Xmx190M \
    -jar reactive-hello-world.jar

EXPOSE 8080

我是不是哪里漏了一步?

编辑:这是一些图片

负载测试前:

负载测试后

如您所见,GC 正常进行,但内存并未减少。如果我让测试继续,它会在几分钟内杀死实例。

我已经使用 RestTemplate 尝试了类似的代码,但我没有遇到任何问题,内存通常不会超过 400MB,即使我长时间 运行 Jmeter 也是如此。你能帮助理解发生了什么吗?

编辑:我也尝试过已弃用的 AsyncRestTemplate,我也没有发现它有问题。

编辑:我已经为此示例创建了存储库。请检查您是否可以重现该问题。

The Hello World Backend

The Webclient Hello World(JMX 在此存储库中)

The RestTemplate Hello World

The AsyncRestTemplate Hello World

我认为您的问题与 RestTemplateWebClient 没有任何关系。您的 GC 图看起来很正常。看起来没有泄漏,因为每当 GC 发生时,分配的内存似乎能够回到以前的水平。

重要的是要注意,当发生垃圾回收时,您不会总是看到容器内存使用率下降。这是因为 Java 虚拟机在 GC 后不一定 return 内存返回系统。这意味着,即使一部分内存在您的进程中的 GC 之后 unused/freed,它仍可能从进程外部显示为“已使用”。

明确一点:在某些情况下,JVM 确实 return 将内存返回给系统,这取决于各种因素,包括所使用的垃圾收集器。

从你的例子的第二张截图的GC图表来看,GC图表看起来很正常,但似乎有相当少量的堆被释放回系统(橙色区域)。

您可以尝试通过 -XX:+UseG1GC JVM 标志切换到 G1 GC,并调整行为以通过调整 -XX:MinHeapFreeRatio-XX:MaxHeapFreeRatio 更积极地将未分配的内存释放回系统,特别是通过将 -XX:MaxHeapFreeRatio 值从默认的 70% 减少到一个较低的数字。参见 here

Lowering -XX:MaxHeapFreeRatio to as low as 10% and -XX:MinHeapFreeRatio has shown to successfully reduce the heap size without too much performance degradation; however, results may vary greatly depending on your application. Try different values for these parameters until they're as low as possible, yet still retain acceptable performance.

有关该主题的更多信息,请参阅:

Does GC release back memory to OS?

JEP 346: Promptly Return Unused Committed Memory from G1

没关系,小伙子们,我找到了答案,请参阅:

https://github.com/reactor/reactor-netty/issues/1304

本质上,reactor netty 依赖已经过时了。