Java - 保存/恢复调用堆栈

时间:2014-05-21 13:31:33

标签: java callstack

是否可以在执行期间保存调用堆栈的状态,然后将堆栈恢复到该状态,在最顶层的方法调用中重新启动?

5 个答案:

答案 0 :(得分:10)

我强烈建议改变你的范例。而不是操纵调用堆栈(并且很可能破坏你的JVM),考虑在算法中以某种方式合并“调用堆栈”。

您可以使用策略/命令/撤消设计模式并将对象存储到您自己的逻辑堆栈,您可以完全控制它们。

有一个相关问题Way to go from recursion to iteration有很多好的答案。

答案 1 :(得分:3)

您可以使用正常的程序流控制从特定的调用堆栈重新启动,而不是直接与调用堆栈混乱。一种方法是例外:

Result findResult() {
    while (true) {
        Data data = getData();
        try {
            return processData(data);
        } catch (OutOfDateException e) {
            /* fall through */
        }
    }
}

Result processData(Data data) throws OutOfDateException {
    /* ... */
    processData2(otherData)
    /* ... */
}

IntermediateResult processData2(OtherData otherData) throws OutOfDateException {
    /* ... */
    if (myDataWasTooOld())
        throw new OutOfDateException();
    /* ... */
}

在该示例中,如果processData2()抛出异常,则使用processData2()的新值重新创建相同的调用堆栈(processData()调用data)。

答案 2 :(得分:0)

保存当前调用堆栈非常简单。 Thread类具有一些面向堆栈跟踪的函数。很可能这就是你要找的 -

StackTraceElement[] stack = Thread.currentThread().getStackTrace();

现在您已经保存了堆栈跟踪,以便稍后使用。我不确定你的意思是恢复堆栈跟踪。

答案 3 :(得分:0)

绝对可以观察堆栈跟踪。在完成太多的shell脚本并为每行代码获取响应之前,我已经完成了这项工作。我所做的是创建一个监听器线程,它是主线程的子级。

这样的事情:

public class Debugger implements Serializable {
    private static final long serialVersionUID = 1L;

    private Thread parent;
    private String packageName;
    private File outputFile;
    private boolean alive;

    public Debugger(Thread parent, String packageName, File outputFile) {
        this.parent = parent;
        this.packageName = packageName;
        this.outputFile = outputFile;
        alive = true;
    }

    public void run() throws FileNotFoundException {
        PrintStream ps = new PrintStream(outputFile);
        alive = true;
        String lastClassName = "";
        while (parent.isAlive() && alive) {
            for (StackTraceElement ste : parent.getStackTrace()) {
                if (ste.getClassName().startsWith(packageName)) {
                    if (!ste.getClassName().equals(lastClassName)) {
                        ps.println(ste.toString());
                        lastClassName = ste.getClassName();
                    }
                    break;
                }
            }
        }
        ps.close();
    }

    public void kill() {
        alive = false;
    }
}

如果在要调试的代码的开头创建此对象,它将在您调用的包前缀/子字符串中的每个新方法中写入您的文件。然后只需要在要监视堆栈跟踪的代码上调用kill()。如果你的主线程/代码运行得比这个线程快,它有可能会错过方法,但它很可能会捕获所有内容。

您可以在其中添加时间戳(新的日期()),以查看您的方法调用返回的时间。

为了得到你想要的东西,你可以在堆栈跟踪中保存这些方法调用,并使用反射稍后再次调用这些方法。但是,我确实认为这是一种不好的做法。

答案 4 :(得分:0)

我认为你正在寻找基于Continuations的东西... Java Flow是一个古老的项目,但我认为它完全符合你的要求 - 使用字节码“魔术”将堆栈帧保存为Continuation对象 - 也就是说,一些额外的代码被编织到您的代码中,以允许将堆栈帧保存到对象中。

http://commons.apache.org/sandbox/commons-javaflow/tutorial.html