Why does Spring's RestTemplate use an excessive amount of heap (particularly the G1 Old Generation
) when sending a file.
We observed the RestTemplate to consume excessive amounts of memory when sending files via POST
requests. We used Spring's WebClient as comparison and it behaves completely sane.
We created a demo project on github which contains the full code. The important parts are the following snippets:
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);
}
and
private void sendFileAsOctetStream(File file) {
webClient.post()
.uri("/file")
.body(BodyInserters.fromResource(new FileSystemResource(file)))
.exchange()
.block();
}
We observed the memory usage with jconsole
when sending a 550MB file with both the implementations (left is WebClient
, right is RestTemplate
. The WebClient
cosumes a couple of MegaBytes while the RestTemplate
requires 2.7 GigaByte:
RestTemplate
)RestTemplate provides a synchronous way of consuming Rest services, which means it will block the thread until it receives a response. RestTemplate is deprecated since Spring 5 which means it's not really that future proof.
Compared to RestTemplate , WebClient has a more functional feel and is fully reactive. Since Spring 5.0, RestTemplate is deprecated. It will probably stay for some more time but will not have major new features added going forward in future releases. So it's not advised to use RestTemplate in new code.
WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward. We are writing a new project using spring boot 2.0.
RestTemplate uses Java Servlet API and is therefore synchronous and blocking. Conversely, WebClient is asynchronous and will not block the executing thread while waiting for the response to come back. The notification will be produced only when the response is ready. RestTemplate will still be used.
This is due to the default RestTemplate
which simply uses an unconfigured SimpleClientHttpRequestFactory
for the creation of the requests.
The mentioned requst factory has a flag bufferRequestBody
which by default is set to true
, which leads to very high memory consumption when sending large requests.
From the javadoc of SimpleClientHttpRequestFactory#setBufferRequestBody()
:
Indicate whether this request factory should buffer the request body internally. Default is true. When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory. This will result in a ClientHttpRequest that either streams directly to the underlying HttpURLConnection (if the Content-Length is known in advance), or that will use "Chunked transfer encoding" (if the Content-Length is not known in advance).
You can provide your own request factory, when creating the RestTemplate
by using one of the other overloaded constructors, and setting mentioned flag to false
on the request factory:
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory rf = new SimpleClientHttpRequestFactory();
rf.setBufferRequestBody(false);
return new RestTemplate(rf);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With