Java thread 증가로 인한 OutOfMemoryError 발생 원인
Java에서 thread 수 증가로 인한 OutOfMemoryError는 힙 메모리가 부족해서가 아니라, OS 레벨에서 할당 가능한 스레드 자원이 한계에 도달했을 때 발생합니다. 즉, 애플리케이션 내부 문제가 아니라 JVM과 OS가 함께 관리하는 영역의 한계라고 보면 됩니다.
Thread가 메모리를 사용하는 방식
Java에서 스레드를 생성하면 단순히 객체 하나가 만들어지는 것이 아니라, 각 스레드는 별도의 Stack 메모리를 할당받습니다. 이 Stack 영역은 메서드 호출, 지역 변수 등을 저장하는 공간입니다.
기본적으로 JVM은 스레드마다 일정 크기의 Stack을 할당하는데, 이 값은 JVM 옵션으로 조정할 수 있습니다.
-Xss1m
위 옵션은 스레드 하나당 1MB의 Stack을 사용하겠다는 의미입니다. 이 경우 스레드가 1000개라면 단순 계산으로도 약 1GB의 메모리가 필요하게 됩니다.
핵심 포인트
스레드 수가 많아질수록 힙이 아니라 Stack 메모리가 누적된다는 점이 중요합니다. 이 부분을 놓치면 GC 튜닝이나 Heap 사이즈 조정으로 문제를 해결하려고 하게 됩니다.
OutOfMemoryError가 발생하는 실제 이유
thread 관련 OutOfMemoryError는 대부분 다음 메시지 형태로 나타납니다.
java.lang.OutOfMemoryError: unable to create new native thread
이 에러는 JVM이 새로운 스레드를 생성하려 했지만, OS에서 더 이상 스레드를 생성할 수 없는 상태라는 의미입니다.
여기서 중요한 점은 메모리 부족이라기보다 "스레드 생성 한도 초과"에 가깝다는 것입니다.
OS 레벨 제약
스레드는 결국 OS의 리소스를 사용합니다. 따라서 다음 요소들이 제한 조건이 됩니다.
- 프로세스당 최대 스레드 수
- 전체 시스템의 메모리
- ulimit 설정
이 부분은 JVM 설정만으로 해결되지 않는 경우가 많습니다.
실무에서 자주 발생하는 패턴
스레드 수 증가 문제는 대부분 의도적으로 스레드를 많이 생성해서 발생하기보다는, 관리되지 않은 구조에서 점진적으로 증가하는 경우가 많습니다.
1. Thread를 직접 생성하는 코드
new Thread(() -> {
// 작업 수행
}).start();
이 방식은 단순하지만, 제어 없이 계속 생성되면 스레드 수가 빠르게 증가합니다. 특히 반복문 안에서 사용하면 문제를 만들기 쉽습니다.
2. Thread Pool 미사용 또는 오용
Thread Pool을 사용하지 않거나, 최대 크기를 과도하게 설정한 경우에도 유사한 문제가 발생합니다.
실무에서는 스레드 풀을 사용하더라도 queue가 아닌 thread 수를 늘리는 방향으로 설정하는 경우가 있는데, 이때도 스레드 수가 통제되지 않습니다.
3. 외부 라이브러리 내부 스레드
라이브러리 내부에서 생성하는 스레드는 코드상으로 드러나지 않기 때문에 놓치기 쉽습니다. 특히 scheduler, async 처리 라이브러리에서 이런 경우가 자주 보입니다.
해결 접근 방법
이 문제는 단순히 메모리를 늘리는 방식으로 해결되지 않습니다. 스레드 사용 방식을 구조적으로 점검해야 합니다.
1. Thread Pool 사용
ExecutorService executor = Executors.newFixedThreadPool(10);
스레드를 재사용하는 구조로 바꾸는 것이 기본적인 해결 방법입니다. 이 방식은 생성 비용뿐 아니라 개수 제어에도 효과적입니다.
2. 스레드 수 제한 전략
동시에 처리할 수 있는 작업 수를 제한하는 구조가 필요합니다. 무조건 병렬 처리로 늘리는 것보다, 큐 기반으로 처리하는 방식이 더 안정적입니다.
3. Stack Size 조정
-Xss 옵션을 줄이면 동일한 메모리에서 더 많은 스레드를 생성할 수 있습니다. 다만 StackOverflowError 가능성이 있기 때문에 무조건 줄이는 것은 권장하지 않습니다.
정리
thread 증가로 인한 OutOfMemoryError는 힙 문제가 아니라 스레드 자원 한계 문제입니다.
핵심은 다음 세 가지입니다.
- 스레드는 Stack 메모리를 사용한다
- OS 레벨에서 생성 가능한 스레드 수는 제한되어 있다
- 구조적으로 스레드 수를 제어해야 한다
단순히 메모리 옵션을 조정하기보다, 스레드를 어떻게 생성하고 관리하는지를 먼저 점검하는 것이 문제 해결의 출발점입니다.
'개발 > JAVA' 카테고리의 다른 글
| [JAVA] GC 로그 분석으로 성능 문제 해결한 과정 (0) | 2026.05.04 |
|---|---|
| [JAVA] CPU 100% 찍는 Java 프로세스 원인 찾는 방법 (0) | 2026.05.03 |
| [JAVA] Heap dump 분석해서 메모리 누수 잡은 방법 (0) | 2026.05.01 |
| [JAVA] JVM 옵션 잘못 건드렸다가 서비스 장애 경험 (0) | 2026.04.30 |
| [JAVA] G1GC로 바꿨는데 오히려 성능이 나빠진 이유 (0) | 2026.04.29 |
