为什么我用java.lang.Throwable获取不同的堆栈跟踪#getStackTrace和java.lang.Thread #getStackTrace

时间:2018-01-31 12:13:29

标签: java jvm

这是代码:

package stacktrace.test;

public class A {
    public static void main(String[] args) {
        B.f();
    }
}

interface B {
    static void f() {
        C.f();
    }
}

interface C {
    static void f() {
        StackTraceElement[] stackTrace1 = (new Exception()).getStackTrace();
        StackTraceElement[] stackTrace2 = Thread.currentThread().getStackTrace();
        StackTraceElement x1 = stackTrace1[1];
        StackTraceElement x2 = stackTrace2[1];
        System.out.println(x1.getClassName());
        System.out.println(x2.getClassName());
    }
}

输出:

stacktrace.test.B
stacktrace.test.C

我检查它调用的Thread.currentThread().getStackTrace() (new Exception()).getStackTrace();。在这种情况下,为什么我会得到不同的结果?

提前致谢!

2 个答案:

答案 0 :(得分:1)

要了解发生了什么,请首先逐项打印整个堆栈跟踪。以下是您要获得的内容(demo):

-- stackTrace1
C.f(Main.java:23)
B.f(Main.java:17)
A.main(Main.java:11)

-- stackTrace2
java.lang.Thread.getStackTrace(Thread.java:1556)
C.f(Main.java:24)
B.f(Main.java:17)
A.main(Main.java:11)

请注意,唯一的区别在于两个堆栈跟踪的初始项:第二个在顶部包含一个额外的框架。

填充Throwable堆栈的方法似乎用Throwable或其子类的构造函数排除了框架,以及在构建它的过程中调用的其他方法。

这是有道理的,因为当您看到异常的堆栈跟踪时,您希望在顶部看到throw方法,并忽略创建异常需要您调用其构造函数的事实。

另一方面,

线程的getStackTrace()方法不会将自己从堆栈中移除。这就是你打印出来时在堆栈顶部看到的内容。

答案 1 :(得分:1)

由于您已经正确地注意到自己,Thread.currentThread().getStackTrace()调用了(new Exception()).getStackTrace();这意味着前者的堆栈跟踪只会延长一帧。

如果一个方法调用另一个方法,则根据定义,它们的堆栈跟踪不能相同。