Java如何生成线程的堆栈跟踪?
示例:
考虑 var vendor = [
"bower_components/jquery/dist/jquery.min.js",
"bower_components/angular/angular.min.js",
"bower_components/angular-route/angular-route.min.js",
"bower_components/angular-ui-router/release/angular-ui-router.min.js",
"bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js",
"bower_components/jquery-steps/build/jquery.steps.min.js",
"bower_components/angular-resource/angular-resource.min.js" ];
uglify: {
options: {
mangle: false,
compress: {
drop_console: true
}
},
dist: {
files: {
'build/vendor/vendor.min.js': vendor,
'build/js/allModules.min.js': modFiles,
'build/js/restFiles.min.js': userFiles
}
}
}});
次来电functionA
来电functionB
来电functionC
。如果在functionD
functionD
中的任何一点使用它,它将给出functionCalls数组:
getStackTraceElementArray
Java如何在运行时填充functionC->functionB->functionA
数组?假设它到达被调用函数内部时填充调用函数,JVM如何在被调用方法中获取调用方法的引用?
答案 0 :(得分:4)
在最简单的情况下,堆栈跟踪是从堆栈中获得的!
每个线程都有一个帧指针(FP)寄存器,指向当前堆栈帧的基址。调用Java方法时,它首先创建一个新的堆栈帧,即它将一堆信息推送到堆栈中:
然后它更新FP以指向新创建的帧。
这样,你看,帧指针构成一个链表:如果我读取FP指向的值,我得到前一帧的基地址。
现在请注意,对于每个帧,方法ID始终与FP的偏移量相同。因此,堆栈遍历就像while
循环一样简单(用伪代码编写):
address fp = currentThread.getFP();
while (fp != null) {
methodID m = (methodID) read_stack_at(fp + METHOD_OFFSET);
print_method(m);
fp = (address) read_stack_at(fp);
}
它是如何在JVM中为解释方法工作的。编译方法有点复杂。它们通常不会在堆栈上保存方法引用。相反,有一种结构将已编译代码的地址映射到包含已编译方法信息的元数据。但是堆栈行走的想法仍然是一样的。