This is a demonstrator to show how streaming a large requests with spring used to work, but is now buffering the whole object in memory.
- mvn clean install (Apache Maven 3.9.11)
- Launch the spring boot application via
com.github.manveroo.spring_web_streaming.SpringWebStreamingApplication. - Don't forget to limit the available memory by adding
-Xmx500m. - Open the UI on http://localhost:8080/
- Click the button "Start Streaming Working" to stream a temp file without any issues since it uses the old
implementation or use
curl -XPOST http://localhost:8080/startWorkingto trigger the endpoint. - Click the button "Start Streaming Broken" to stream a temp file and end up in a
java.lang.OutOfMemoryError: Java heap spaceerror or usecurl -XPOST http://localhost:8080/startBrokento trigger the endpoint. - Code is found in the package:
com.github.manveroo.spring_web_streaming.Controller
Any version of spring-web starting with 6.1.x is using a FastByteArrayOutputStream while processing a request at:
AbstractClientHttpRequest:getBody -> AbstractStreamingClientHttpRequest:getBodyInternal -> new FastByteArrayOutputStream
Before (e.g. spring-web version 6.0.23),
AbstractClientHttpRequest:getBody -> SimpleStreamingClientHttpRequest:getBodyInternal -> HttpURLConnection:getOutputStream
The implementation using the FastByteArrayOutputStream is buffering the whole request in memory which will trigger an
OOM exception at some point when the request is too large. E.g.:
...
Processed 268238848 bytes
Processed 268304384 bytes
Processed 268369920 bytes
2025-10-15T13:42:46.654+02:00 ERROR 19436 --- [spring-web-streaming] [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.OutOfMemoryError: Java heap space] with root cause
java.lang.OutOfMemoryError: Java heap space
at org.springframework.util.FastByteArrayOutputStream.addBuffer(FastByteArrayOutputStream.java:323) ~[spring-core-6.2.11.jar:6.2.11]
at org.springframework.util.FastByteArrayOutputStream.write(FastByteArrayOutputStream.java:118) ~[spring-core-6.2.11.jar:6.2.11]
at com.github.manveroo.spring_web_streaming.Controller.doStream(Controller.java:138) ~[classes/:na]
at com.github.manveroo.spring_web_streaming.Controller.lambda$startBroken$0(Controller.java:84) ~[classes/:na]
at com.github.manveroo.spring_web_streaming.Controller$$Lambda/0x000001ed0151eb38.doWithRequest(Unknown Source) ~[na:na]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:898) ~[spring-web-6.2.11.jar:6.2.11]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:801) ~[spring-web-6.2.11.jar:6.2.11]
at com.github.manveroo.spring_web_streaming.Controller.startBroken(Controller.java:90) ~[classes/:na]
at java.base/java.lang.invoke.LambdaForm$DMH/0x000001ed01308400.invokeVirtual(LambdaForm$DMH) ~[na:na]
at java.base/java.lang.invoke.LambdaForm$MH/0x000001ed01335000.invoke(LambdaForm$MH) ~[na:na]
at java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder) ~[na:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:153) ~[na:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) ~[spring-web-6.2.11.jar:6.2.11]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) ~[spring-web-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:991) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:896) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.11.jar:6.2.11]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.2.11.jar:6.2.11]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.46.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.2.11.jar:6.2.11]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.46.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[tomcat-embed-core-10.1.46.jar:10.1.46]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.46.jar:10.1.46]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.46.jar:10.1.46]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.46.jar:10.1.46]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.46.jar:10.1.46]
