java - 为什么 RestTemplate 会消耗过多的内存?
问题描述
问题
为什么 Spring 的 RestTemplateG1 Old Generation
在发送文件时会使用过多的堆(尤其是 )。
语境
我们观察到RestTemplatePOST
在通过请求发送文件时会消耗过多的内存。我们使用 Spring 的WebClient作为比较,它的行为完全正常。
我们在 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();
}
jconsole
我们观察了使用两种实现发送一个 550MB 文件时的内存使用情况(左边是WebClient
,右边是RestTemplate
。WebClient
消耗几兆字节,而RestTemplate
需要 2.7 千兆字节:
- 用于清理老年代的初始手动 GC
- 请求
- 手动 GC(仅适用于
RestTemplate
)
解决方案
这是由于默认情况下RestTemplate
简单地使用未配置SimpleClientHttpRequestFactory
来创建请求。
上面提到的请求工厂有一个bufferRequestBody
默认设置为的标志true
,这会导致在发送大请求时内存消耗非常高。
来自的javadoc SimpleClientHttpRequestFactory#setBufferRequestBody()
:
指示此请求工厂是否应在内部缓冲请求正文。默认为真。通过 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);
}
推荐阅读
- node.js - 下载具有自定义名称 expressjs 的文件
- python - TemplateDoesNotExist at / 模板路径
- java - 满足特定条件时如何删除java方法参数
- python - 将对象属性从 id 变为实际 id 引用的好方法是什么
- android - IllegalArgumentException:只能将 TabItem 实例添加到 TabLayout
- excel - Excel公式通过在另一个列表中找到的查找值来汇总在一个列表中找到的金额
- r - 如何在R中进行联合重采样
- apache-spark - Spark 任务数(分区)不等于 spark.default.parallelism
- r - 修改 DataTables 包中的 R Shiny 搜索栏外观和位置
- amazon-web-services - 跨多个 EKS 集群共享子网