jvm 꾸준히 - 스택오버플로/메모리오버플로/metaspace

김태훈·2026년 4월 14일

stack 이란

스레드가 생겨날때, stack도 같이 생긴다.
이 때, 그 스택 안에는 해당 스레드에서 호출된 메서드로부터 만들어진 스택프레임이 채워진다.

StackOverFlow

왜 발생하는가?
재귀 호출이 너무 깊어지거나, 각 호출 프레임이 커서 현재 스레드의 stack 공간 안에 더 이상 새로운 frame을 넣을 수 없을 때 발생한다.

클래식 VM에서는 동적으로 스택의 크기를 확장할 수 있어서 OOM이 났었다. 하지만 핫스팟 가상머신으로 들어와서부터는 아님!

스레드가 많이생겨서 OOM이 나는것을 해결하는 방법

Heap memory 줄이고, 스레드 스택사이즈 줄이고..!

JDK 버전 차이에 따른

JDK 6 및 그 이전

String intern pool이 PermGen(메서드 영역 계열) 쪽에 있었음
그래서 intern() 동작이 상대적으로 부담스럽고
perm 영역 압박 문제가 있었음

JDK 7 이후

String intern pool이 heap으로 이동
그래서 intern() 관련 동작 특성이 달라짐
더 이상 예전 PermGen 제약과 같은 식으로 보지 않음

JDK 8 이후

PermGen 자체가 사라지고 Metaspace 사용
메타스페이스는 native memory 사용
하지만 String Pool은 heap 쪽으로 보면 됨

동적 프록시 사용으로 인한 MetaSpace 공간 사용 증가

동적 프록시를 많이 쓰거나 동적으로 클래스를 생성하면 metaspace 사용량에 영향이 갈 수 있다.
왜냐하면 CGLIB이나 JDK 동적 프록시는 런타임에 프록시 클래스를 만들거나 로딩한다.
그럼 결국 JVM은 그 클래스 메타정보를 관리해야 하고, 그런 정보는 method area / metaspace 계열에 들어가게 된다.

정리

JVM에서 스레드마다 하나의 stack이 있고, 메서드 호출마다 stack frame이 쌓인다.
StackOverflowError는 한 스레드의 stack 공간 안에서 새로운 frame을 더 할당할 수 없을 때 발생한다.
반면 OutOfMemoryError는 heap, metaspace, thread stack 총합 등 프로세스 전체 메모리가 부족할 때 발생한다.
또한 Runtime Constant Pool과 String Pool은 같은 것이 아니다. Runtime Constant Pool은 클래스의 상수 정보를 위한 영역이고, String Pool은 문자열 객체 공유를 위한 풀이다. 현대 JVM에서는 String Pool은 heap에, 클래스 메타정보는 Metaspace(native memory)에 저장된다. String.intern()은 같은 문자열이 풀에 있으면 그 참조를 반환하고, 없으면 풀에 등록한다.
또한 Spring의 @Transactional, @Async, @Cacheable 등은 프록시 기반으로 동작하며, CGLIB/JDK Dynamic Proxy처럼 런타임에 생성되는 프록시 클래스들은 metaspace 사용량에 영향을 줄 수 있다.(곁들이기)

profile
기록하고, 공유합시다

0개의 댓글