サーチ…


前書き

Java 9より前では、スレッドスタックフレームへのアクセスは、内部クラスsun.reflect.Reflectionに制限されていました。具体的には、 sun.reflect.Reflection::getCallerClass 。一部のライブラリはこのメソッドに依存していますが、非推奨です。

代替標準APIは現在経由JDK 9に設けられているjava.lang.StackWalkerクラス、およびスタックフレームに怠惰なアクセスを可能にすることにより、効率的に設計されています。一部のアプリケーションでは、このAPIを使用してクラスの実行スタックとフィルタをトラバースすることがあります。

現在のスレッドのすべてのスタックフレームを出力する

以下は、現在のスレッドのすべてのスタックフレームを出力します。

1  package test;
2  
3  import java.lang.StackWalker.StackFrame;
4  import java.lang.reflect.InvocationTargetException;
5  import java.lang.reflect.Method;
6  import java.util.List;
7  import java.util.stream.Collectors;
8  
9  public class StackWalkerExample {
10 
11    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
12        Method fooMethod = FooHelper.class.getDeclaredMethod("foo", (Class<?>[])null);
13        fooMethod.invoke(null, (Object[]) null);
14    }
15 }
16
17 class FooHelper {
18    protected static void foo() {
19        BarHelper.bar();
20    }
21 }
22 
23 class BarHelper {
24    protected static void bar() {
25        List<StackFrame> stack = StackWalker.getInstance()
26                .walk((s) -> s.collect(Collectors.toList()));
27        for(StackFrame frame : stack) {
28            System.out.println(frame.getClassName() + " " + frame.getLineNumber() + " " + frame.getMethodName());
29        }
30    }
31 }

出力:

test.BarHelper 26 bar
test.FooHelper 19 foo
test.StackWalkerExample 13 main

現在の呼び出し元クラスを表示する

以下は、現在の呼び出し元クラスを出力します。この場合、 StackWalkerオプションRETAIN_CLASS_REFERENCEで作成する必要があるため、 ClassインスタンスはStackFrameオブジェクトに保持されます。それ以外の場合は例外が発生します。

public class StackWalkerExample {

    public static void main(String[] args) {
        FooHelper.foo();
    }

}

class FooHelper {
    protected static void foo() {
        BarHelper.bar();
    }
}

class BarHelper {
    protected static void bar() {
        System.out.println(StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass());
    }
}

出力:

class test.FooHelper

反射などの隠しフレームの表示

いくつかのオプションでは、スタックトレースに実装フレームやリフレクションフレームを含めることができます。これは、デバッグの目的に役立ちます。たとえば、作成時にSHOW_REFLECT_FRAMESオプションをStackWalkerインスタンスに追加すると、反射メソッドのフレームも同様に出力されます。

package test;

import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;

public class StackWalkerExample {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method fooMethod = FooHelper.class.getDeclaredMethod("foo", (Class<?>[])null);
        fooMethod.invoke(null, (Object[]) null);
    }
}

class FooHelper {
    protected static void foo() {
        BarHelper.bar();
    }
}

class BarHelper {
    protected static void bar() {
        // show reflection methods
        List<StackFrame> stack = StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES)
                .walk((s) -> s.collect(Collectors.toList()));
        for(StackFrame frame : stack) {
            System.out.println(frame.getClassName() + " " + frame.getLineNumber() + " " + frame.getMethodName());
        }
    }
}

出力:

test.BarHelper 27 bar
test.FooHelper 20 foo
jdk.internal.reflect.NativeMethodAccessorImpl -2 invoke0
jdk.internal.reflect.NativeMethodAccessorImpl 62 invoke
jdk.internal.reflect.DelegatingMethodAccessorImpl 43 invoke
java.lang.reflect.Method 563 invoke
test.StackWalkerExample 14 main

一部のリフレクションメソッドの行番号は使用できないため、 StackFrame.getLineNumber()は負の値を返す可能性があることに注意してください。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow