为什么 RestTemplate 会消耗过多的内存?问题上下文

2022-09-04 03:54:14

问题

为什么Spring的RestTemplate在发送文件时使用过多的堆(特别是)。G1 Old Generation

上下文

我们观察到 RestTemplate 在通过请求发送文件时会消耗过多的内存。我们使用Spring的WebClient作为比较,它的行为完全理智。POST

我们在github上创建了一个包含完整代码的演示项目。重要部分是以下代码段:

private void sendFileAsOctetStream(File file) {
    final RequestEntity<FileSystemResource> request = RequestEntity.post(URI.create("http://localhost:8080/file"))
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(new FileSystemResource(file));
    restTemplate.exchange(request, void.class);
}

private void sendFileAsOctetStream(File file) {
    webClient.post()
            .uri("/file")
            .body(BodyInserters.fromResource(new FileSystemResource(file)))
            .exchange()
            .block();
}

我们观察了在发送具有两种实现的550MB文件时的内存使用情况(左边是,右边是。它包含几个兆字节,而需要 2.7 千兆字节:jconsoleWebClientRestTemplateWebClientRestTemplate

enter image description here

  1. 用于清洁旧一代产品的初始手动GC
  2. 请求
  3. 手动 GC(仅适用于RestTemplate)

答案 1

这是由于默认设置只是使用未配置的方法来创建请求。RestTemplateSimpleClientHttpRequestFactory

上面提到的 requst 工厂有一个标志,默认情况下设置为 ,这在发送大型请求时会导致非常高的内存消耗。bufferRequestBodytrue

从以下的 javadoc 中:SimpleClientHttpRequestFactory#setBufferRequestBody()

指示此请求工厂是否应在内部缓冲请求正文。默认值为 true。通过 POST 或 PUT 发送大量数据时,建议将此属性更改为 false,以免内存不足。这将导致一个 ClientHttpRequest,该请求要么直接流式传输到底层的 HttpURLConnection(如果内容长度是事先知道的),要么将使用“分块传输编码”(如果内容长度事先未知)。

在创建时,可以使用其他重载构造函数之一创建请求工厂,并在请求工厂上将提到的标志设置为:RestTemplatefalse

@Bean
public RestTemplate restTemplate() {
    SimpleClientHttpRequestFactory rf = new SimpleClientHttpRequestFactory();
    rf.setBufferRequestBody(false);
    return new RestTemplate(rf);
}

答案 2

推荐