Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java StackWalker API로 다양한 스택 프레임 출력하는 방법

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() 방식보다 성능 부담이 적으면서 필요한 프레임만 선택적으로 가져올 수 있어, 로깅·디버깅·성능 분석 작업에서 큰 강점을 발휘합니다.