Avoid Out‑of‑Memory Errors: Properly Configure Spring Boot Async Thread Pool
This article explains why using @Async in Spring Boot can cause memory overflow when many requests trigger parallel tasks, examines the default thread‑pool settings that lead to unbounded queues, and provides step‑by‑step configuration of a safe thread pool to prevent out‑of‑memory failures.
In the previous article we introduced how to use the @Async annotation to create asynchronous tasks in Spring Boot. While this can speed up execution, invoking the endpoint frequently creates a large number of async tasks, which may lead to memory overflow because the default thread pool uses an unbounded queue and maximum thread count.
@RestController
public class HelloController {
@Autowired
private AsyncTasks asyncTasks;
@GetMapping("/hello")
public String hello() {
// Split parallelizable logic into three async tasks
CompletableFuture<String> task1 = asyncTasks.doTaskOne();
CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
CompletableFuture<String> task3 = asyncTasks.doTaskThree();
CompletableFuture.allOf(task1, task2, task3).join();
return "Hello World";
}
}When a single request calls this endpoint there is no problem, but with many concurrent requests the number of async tasks grows to 3 × n (where n is the request count). If task processing is not fast enough, memory overflow occurs. The root cause is the default Spring Boot async thread‑pool configuration, which sets queueCapacity and maxSize to Integer.MAX_VALUE (2³¹‑1).
The two critical parameters to watch are: queueCapacity: capacity of the buffer queue, default is Integer.MAX_VALUE. maxSize: maximum number of threads, also default Integer.MAX_VALUE.
Configuring the Default Thread Pool
Adjust the thread‑pool settings in application.properties (or application.yml) to prevent memory overflow:
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.thread-name-prefix=task-The meaning of each property: spring.task.execution.pool.core-size: initial number of threads when the pool is created (default 8). spring.task.execution.pool.max-size: maximum number of threads (default Integer.MAX_VALUE). spring.task.execution.pool.queue-capacity: size of the task buffer queue (default Integer.MAX_VALUE). spring.task.execution.pool.keep-alive: idle time before a thread is terminated. spring.task.execution.pool.allow-core-thread-timeout: whether core threads are allowed to time out. spring.task.execution.shutdown.await-termination: whether to wait for remaining tasks before shutting down. spring.task.execution.shutdown.await-termination-period: maximum wait time for remaining tasks. spring.task.execution.thread-name-prefix: prefix for thread names, useful for log tracing.
Hands‑On Test
First run a unit test without custom thread‑pool configuration:
@Test
public void test1() throws Exception {
long start = System.currentTimeMillis();
CompletableFuture<String> task1 = asyncTasks.doTaskOne();
CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
CompletableFuture<String> task3 = asyncTasks.doTaskThree();
CompletableFuture.allOf(task1, task2, task3).join();
long end = System.currentTimeMillis();
log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
}Log output (core pool size 8, so all three tasks start simultaneously):
2021-09-15 00:30:14.819 INFO 77614 --- [task-2] com.didispace.chapter76.AsyncTasks : 开始做任务二
2021-09-15 00:30:14.819 INFO 77614 --- [task-3] com.didispace.chapter76.AsyncTasks : 开始做任务三
2021-09-15 00:30:14.819 INFO 77614 --- [task-1] com.didispace.chapter76.AsyncTasks : 开始做任务一
2021-09-15 00:30:15.491 INFO 77614 --- [task-2] com.didispace.chapter76.AsyncTasks : 完成任务二,耗时:672毫秒
2021-09-15 00:30:19.496 INFO 77614 --- [task-3] com.didispace.chapter76.AsyncTasks : 完成任务三,耗时:4677毫秒
2021-09-15 00:30:20.443 INFO 77614 --- [task-1] com.didispace.chapter76.AsyncTasks : 完成任务一,耗时:5624毫秒
2021-09-15 00:30:20.443 INFO 77614 --- [main] c.d.chapter76.Chapter76ApplicationTests : 任务全部完成,总耗时:5653毫秒Now add the custom thread‑pool configuration shown above and rerun the test:
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.thread-name-prefix=task-Log output changes to:
2021-09-15 00:31:50.013 INFO 77985 --- [task-1] com.didispace.chapter76.AsyncTasks : 开始做任务一
2021-09-15 00:31:50.013 INFO 77985 --- [task-2] com.didispace.chapter76.AsyncTasks : 开始做任务二
2021-09-15 00:31:52.452 INFO 77985 --- [task-1] com.didispace.chapter76.AsyncTasks : 完成任务一,耗时:2439毫秒
2021-09-15 00:31:52.452 INFO 77985 --- [task-1] com.didispace.chapter76.AsyncTasks : 开始做任务三
2021-09-15 00:31:55.880 INFO 77985 --- [task-2] com.didispace.chapter76.AsyncTasks : 完成任务二,耗时:5867毫秒
2021-09-15 00:32:00.346 INFO 77985 --- [task-1] com.didispace.chapter76.AsyncTasks : 完成任务三,耗时:7894毫秒
2021-09-15 00:32:00.347 INFO 77985 --- [main] c.d.chapter76.Chapter76ApplicationTests : 任务全部完成,总耗时:10363毫秒Observation: tasks one and two immediately occupy the two core threads; task three waits in the queue. When task one finishes, its core thread becomes free, pulls task three from the queue, and starts processing it.
Note: Although max-size is set to 5, task three does not create a new thread because the queue is not full. Only when the queue reaches its capacity (10 in this example) will additional threads beyond the core size be created.
For the complete project, see the chapter7-6 module under the 2.x directory of the repository:
GitHub: https://github.com/dyc87112/SpringBoot-Learning/
Gitee: https://gitee.com/didispace/SpringBoot-Learning/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
