为什么RestTemplate会消耗过多的内存?

时间:2020-07-03 11:19:29

标签: java spring spring-mvc resttemplate

问题

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

上下文

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

我们创建了一个demo project on 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文件时,我们观察到jconsole的内存使用情况(左为WebClient,右为RestTemplateWebClient包含两个兆字节,而RestTemplate需要2.7兆字节:

enter image description here

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

1 个答案:

答案 0 :(得分:5)

这是由于默认的RestTemplate仅使用未配置的SimpleClientHttpRequestFactory来创建请求。

上述请求工厂具有标志bufferRequestBody,该标志默认情况下设置为true,在发送大请求时会导致很高的内存消耗。

来自SimpleClientHttpRequestFactory#setBufferRequestBody()的javadoc:

指示此请求工厂是否应在内部缓冲请求主体。 默认为true。 通过POST或PUT发送大量数据时,建议将此属性更改为false,以免耗尽内存。这将导致ClientHttpRequest要么直接流到基础HttpURLConnection(如果预先知道Content-Length),要么将使用“块传输编码”(如果事先不知道Content-Length)。

使用其他重载的构造函数之一创建RestTemplate并在请求工厂上将提及的标志设置为false时,您可以提供自己的请求工厂:

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