问题描述
Java 9中提供了Stack Walking API,该API使我们仅能获取最后两个堆栈帧。但是我对Java 8有所了解。在Java 8中,我们可以使用Thread.currentThread().getStackTrace()
或new Throwable().getStackTrace()
来获取堆栈跟踪,但是这两种方法都很慢,因为它们构造了整个堆栈跟踪。有什么方法可以限制堆栈跟踪中的帧数(例如仅构造最后3帧)?
更新:
我测试了Stephen的代码,它确实运行良好。根据选择的深度,它确实将执行时间减少了一个恒定因子。我发现有趣的是,即使堆栈跟踪是在异常创建期间构造并以内部格式存储的,它也不会减少创建新异常的时间。调用StackTraceElement
时,大部分时间都花在将内部格式转换为getStackTrace()
上。因此,如果堆栈跟踪较浅,则转换它所需的时间当然会更少。但是,为什么在创建异常(new Throwable()
)时创建堆栈跟踪的内部表示却花更少的时间呢?
public static void main(String[] args) throws IOException {
f1();
}
private static void f1() {
f2();
}
private static void f2() {
f3();
}
private static void f3() {
f4();
}
private static void f4() {
f5();
}
private static void f5() {
int sum = 0;
long l = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
Throwable throwable = new Throwable();
//comment out to test the internal representation -> StackTraceElement conversion
//StackTraceElement[] trace = throwable.getStackTrace();
//sum += trace.length;
}
long l2 = System.currentTimeMillis();
System.out.println(l2-l);
System.out.println(sum);
}
更新2:为消除Benchamrk问题,我将所有Throwable
实例存储在一个数组中,然后在执行结束时随机检索其中一个实例并打印堆栈跟踪。我认为这将阻止为消除new Throwable()
调用而进行的任何优化。
int sizez = 2000000;
Throwable[] th = new Throwable[sizez];
for (int i = 0; i < sizez; i++) {
th[i] = new Throwable();
//StackTraceElement[] trace = throwable.getStackTrace();
//sum += trace.length;
}
long l2 = System.currentTimeMillis();
System.out.println(l2-l);
System.out.println(sum);
Random rand = new Random();
StackTraceElement[] trace = th[rand.nextInt(sizez)].getStackTrace();
System.out.println(trace[0]);
解决方法
您可以使用JVM选项-XX:MaxJavaStackTraceDepth=depth
来限制堆栈跟踪的大小。
但是,这适用于所有堆栈跟踪,听起来并不像您所需要的那样。我认为没有一种方法可以根据具体情况来限制大小。