Thread Overflow 발생 원인

1,394 단어·3 분·원문(.md)

thread overflow라는 용어는 크게 단일 슬데ㅡ 내부의 메모리 초과(stack overflow)와 시스템 전체의 스레드 생성 한계 초과 thread exhaustion 두 가지 의미로 혼용되어 사용된다.

Thread Overflow란 무엇인가 #

프로그램이 실행될 때 운영체제는 작업을 처리하기 위해 스레드라는 실행 단위를 생성한다.

각 스레드는 함수 호출 내역과 지역 변수등을 저장하기 위해 자신만의 독립적인 스택 메모리 공간을 할당받는다.

스레드 오버플로우또는 관련 장애는 이 스레드 할당된 메모리 공간이 꽉 차거나 시스템이 감당할 수 없을만큼 너무 많은 스레드가 생성되어 시스템 자원이 고갈될 때 발생한다.

이 에러가 발생하면 해당 프로세스(애플리케이션)는 더 이상 정상적인 동작을 하지 못하고 강제종료 되는 경우가 만ㄶ다.

스레드 관련 오버플로우 2가지 주요 원인 #

발생하는 위치, 원인에 따라 크게 두 가지 상황으로 나눌 수 있다.

스택 오버 플로우 #

하나의 스레드에 할당된 스택 메모리 크기는 보통 1~2mb 내외를 초과하여 데이터를 밀어 넣으려 할때 발생한다.

  • 무한 재귀 호출 infinte recursion 같은 함수 종료 조건 없이 자기 자신을 계속 호출하여 스택 프레임이 끝없이 쌓이는 경우 발생
  • 과도한 지역 변수 할당같이 하나의 함수 안에서 너무 큰 크기의 배열이나 객체를 지역 변수로 선언해 스택 메모리를 한 번에 고갈 시키는 경우에도 발생
  • 상호 참조 circular reference같은 a객체가 b를 부르고 b가 다시 a를 부르는 로직이 반복되는 경우 발생

스레드 자원 고갈 thread exhaustion / oom #

스레드 내부의 메모리 문제가 아니라, 애플리케이션이 너무 많은 스레드를 생성하려고 해서 운영체제 os의 제한이나 전체 메모리 한계에 부딪혔을 때 발생한다.

  • 주요원인
    • 스레드 풀 미사용: 트래픽이 들어올때마다 무한정 new Thread()를 생성하도록 잘못 설계된 경우
    • 자원 반환 누락: 스레드가 작업을 마친 후 종료되거나 반환되지 않고 대기 상태 zombie 상태로 계속 누적되는 경우
    • os 제한 도달: 리눅스 등 운영체제에 설정된 최대 생성 가능 프로세스/스레드 수를 초과한 경우

정리하면 스택 오버 플로우는 발생 범위가 단일 스레드 내부 메모리 문제고 java.lang.StackOverflowError 오류가 대표적인 에려명이며 재귀 함수 탈출 조건을 확인하거나 로직을 수정하는 방향으로 접근해야한다.

스레드 자원 고갈은 프로세스 전체의 자원, 스레드 개수 문제며 java.lang.OutOfMemoryError:unable to create new native thread가 대표적인 에러명이며 스레드풀 적용, 톰캣/os 스레드 제한 설정 튜닝을 하는 방식으로 접근해야한다.

예시 자료 및 결과 셋 #

stack overflow 발생 가장 심플한건 종료조건이 없는 재귀함수 호출이다.

public class OverflowExample {
    // 자기 자신을 무한히 호출하는 메서드
    public static void recursiveCall(int number) {
        System.out.println("Current Number: " + number);
        // 종료 조건이 없으므로 계속해서 스택에 쌓임
        recursiveCall(number + 1); 
    }

    public static void main(String[] args) {
        recursiveCall(1);
    }
}
Current Number: 10483
Current Number: 10484
Exception in thread "main" java.lang.StackOverflowError
	at java.base/sun.nio.cs.UTF_8.updatePositions(UTF_8.java:58)
	at OverflowExample.recursiveCall(OverflowExample.java:6)
	at OverflowExample.recursiveCall(OverflowExample.java:6)
	... (수천 줄의 동일한 에러 반복)

스레드 자우너 고갈 thread exhaustion 에러 로그는 애플리케이션이 감당할 수 없는 수의 스레드를 생성했을때 백엔드 로그 예시로 spring boot, tomcat에 다음과 같이 출력된다.

Exception in thread "http-nio-8080-Acceptor" java.lang.OutOfMemoryError: unable to create new native thread
	at java.lang.Thread.start0(Native Method)
	at java.lang.Thread.start(Thread.java:717)
	at org.apache.tomcat.util.net.NioEndpoint.setSocketOptions(NioEndpoint.java:444)

os로 부터 새로운 네이티브 스레드를 할당받지 못하여 메모리 부족 oom이 발생한것이고 트래픽 폭주나 스레드 누수를 의심해야한다.

os 레벨 스레드 한계 확인 및 조치 명령어를 알아보자 스레드 자우너 고갈 에러가 발생했으면, 서버의 os 설정이 너무 낮게 잡혀있는 것은 아닌지 확인하자.

# 1. 특정 유저가 생성할 수 있는 최대 프로세스/스레드 수 확인 (Soft/Hard Limit)
ulimit -u

# 2. 커널 시스템 전체에서 허용하는 최대 스레드 수 확인
cat /proc/sys/kernel/threads-max

# 3. 현재 실행 중인 특정 프로세스(예: PID 1234)가 생성한 스레드 개수 확인
cat /proc/1234/status | grep Threads
출력 결과 예시 (3번 명령어):
Threads:  205 (해석: 현재 1234번 프로세스는 205개의 스레드를 띄우고 있습니다.)
SRE/question/q_39.md