Java 9부터는 지연 평가(laziness)와 프레임 필터링 기능을 갖춘 StackWalker API가 새롭게 도입되었습니다. StackWalker 객체를 사용하면 현재 스레드의 호출 스택을 자유롭게 탐색하고 접근할 수 있으며, 여기서 핵심 역할을 하는 메서드가 바로 walk()입니다.
walk() 메서드는 현재 스레드에 대한 StackFrame 스트림을 열어 해당 스트림에 함수를 적용한 결과를 반환합니다. 사용 방법은 간단합니다. 먼저 StackWalker.getInstance() 메서드로 StackWalker 객체를 얻은 뒤 walk() 메서드를 호출하면 됩니다.
예제 소개
아래 예제에서는 StackWalker API를 활용해 다음 세 가지 방식으로 스택 프레임을 출력합니다.
- 전체 출력(all) : 모든 스택 프레임을 한 번에 확인
- 건너뛰기(skip) : 상위 일부 프레임을 제외하고 출력
- 개수 제한(limit) : 필요한 개수만큼만 출력
getInstance() 옵션 살펴보기
- RETAIN_CLASS_REFERENCE : 각 StackFrame에서 Class 객체 참조를 유지합니다.
- SHOW_HIDDEN_FRAMES : 람다 등 구현 과정에서 숨겨진 프레임까지 표시합니다.
- 두 번째 매개변수(16)는 예상 스택 깊이(estimated depth)로, 내부 버퍼 크기 힌트를 제공해 성능을 최적화하는 데 사용됩니다.
예제 코드
import java.lang.StackWalker.StackFrame;
import java.util.*;
import java.util.stream.*;
public class StackWalkerTest {
public static void main(String args[]) {
new StackWalkerTest().walk();
}
private void walk() {
new Walker1().walk();
}
private class Walker1 {
public void walk() {
new Walker2().walk();
}
}
private class Walker2 {
public void walk() {
Method1();
}
void Method1() {
Method2();
}
void Method2() {
// 옵션 설정 및 예상 스택 깊이(16) 지정
StackWalker stackWalker = StackWalker.getInstance(
Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,
StackWalker.Option.SHOW_HIDDEN_FRAMES), 16);
// 가장 기본적인 사용법 : 현재 스레드의 StackFrame 스트림 얻기
Stream<StackFrame> stackStream = StackWalker.getInstance().walk(f -> f);
System.out.println("--- Walk all StackFrames ---");
List<String> stacks = walkAllStackframes();
System.out.println(stacks);
System.out.println("--- Skip some StackFrames ---");
List<String> stacksAfterSkip = walkSomeStackframes(3);
System.out.println(stacksAfterSkip);
System.out.println("--- Limit StackFrames ---");
List<String> stacksByLimit = walkLimitStackframes(3);
System.out.println(stacksByLimit);
}
// 1) 전체 스택 프레임 출력
private List<String> walkAllStackframes() {
return StackWalker.getInstance().walk(s ->
s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName())
.collect(Collectors.toList()));
}
// 2) 일부 스택 프레임 건너뛰기
private List<String> walkSomeStackframes(int numberOfFrames) {
return StackWalker.getInstance().walk(s ->
s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName())
.skip(numberOfFrames)
.collect(Collectors.toList()));
}
// 3) 스택 프레임 개수 제한
private List<String> walkLimitStackframes(int numberOfFrames) {
return StackWalker.getInstance().walk(s ->
s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName())
.limit(numberOfFrames)
.collect(Collectors.toList()));
}
}
}
실행 결과
--- Walk all StackFrames --- [ StackWalkerTest$Walker2/walkAllStackframes, StackWalkerTest$Walker2/Method2, StackWalkerTest$Walker2/Method1, StackWalkerTest$Walker2/walk, StackWalkerTest$Walker1/walk, StackWalkerTest/walk, StackWalkerTest/main ] --- Skip some StackFrames --- [ StackWalkerTest$Walker2/walk, StackWalkerTest$Walker1/walk, StackWalkerTest/walk, StackWalkerTest/main ] --- Limit StackFrames --- [ StackWalkerTest$Walker2/walkLimitStackframes, StackWalkerTest$Walker2/Method2, StackWalkerTest$Walker2/Method1 ]
결과 해석
- Walk all StackFrames : 현재 실행 중인 walkAllStackframes()부터 main()까지 호출 경로 전체가 출력됩니다.
- Skip some StackFrames : skip(3)에 의해 가장 안쪽 3개 프레임(walkAllStackframes, Method2, Method1)이 제외된 결과를 확인할 수 있습니다.
- Limit StackFrames : limit(3)에 의해 가장 안쪽 3개 프레임만 추출되었습니다.
마무리
StackWalker의 walk() 메서드는 Stream API와 완벽하게 호환되기 때문에 filter(), skip(), limit(), collect() 등 익숙한 스트림 연산을 그대로 활용해 원하는 형태로 스택 정보를 가공할 수 있습니다. 기존의 Thread.getStackTrace()나 Throwable.getStackTrace() 방식보다 성능 부담이 적으면서 필요한 프레임만 선택적으로 가져올 수 있어, 로깅·디버깅·성능 분석 작업에서 큰 강점을 발휘합니다.