Java 调用堆栈检查和操作
Java call stack inspection and manipulation
我的问题是:是否可以(以任何方式)在运行时分析和修改调用堆栈(帧内容和堆栈内容)?
我正在寻找任何可能性 - 低级、不安全或内部 API、编写 C 扩展的可能性等。唯一的限制:它应该在标准运行时可用,无需调试或分析模式.这是我研究的重点 "is it possible at all?",而不是 "is it good idea?"。
我想从帧中收集所有本地数据,将其存储在某处,然后从堆栈中删除该帧,并可能在以后恢复它。实际上,这为我们提供了 JVM 的延续,它们将允许快速异步框架(如 python 中的 gevents)和生成器构造(如 python 中的那些)出现。
这看起来像是重复的问题,但我只找到了用 "use Thread.currentThread().getStackTrace()
" 或 "that should be done with debugging tools" 回答的问题。有similiar question to mine, but it was only answered in context of what asking guy wanted to do (work on async computations), while I need more general (java-stack oriented) answer. This question也是类似的,但是和以前一样,它专注于并行化,答案也专注于此。
我再说一遍:这是提出新语言功能提案过程中的研究步骤。我不想冒险破坏 JVM 中的任何东西 - 我正在寻找可能性,然后我将分析可能的风险并寻找它们。我知道手动操作堆栈是丑陋的,但是使用省略构造函数创建实例也是如此 - 它是 objenesis 的基础。肮脏的技巧可能很肮脏,但它们可能有助于介绍一些很酷的东西。
PS。我知道 Quasar and Lightwolf 存在,但是,如上所述,这些都是以并发为中心的框架。
编辑
一点说明:我正在寻找与未来的 JVM 和库版本兼容的东西。最好我们谈论的是被认为是稳定的东西 public API,但是如果解决方案在于内部的东西,但几乎是标准的或者在成为内部之后成为标准(比如 sun.misc.Unsafe) - 那也会做。如果仅使用 C JVM API 可以通过 C 扩展实现 - 没关系。如果这对于字节码操作是可行的——那也没关系(我认为这可能通过 ASM 实现)。
在此工具中不确定,但您可以查看 http://en.wikipedia.org/wiki/GNU_Debugger。
GDB 提供了广泛的工具来跟踪和更改计算机程序的执行。用户可以监视和修改程序内部变量的值,甚至可以独立于程序的正常行为调用函数。
我认为有一种方法可以使用 JVMTI 实现您想要的。
虽然你不能直接做你想做的事(如上面的评论所述),但你可以 instrument/redefine 方法(或整个 类)在 运行 时间。因此,您可以定义每个方法以直接调用另一个方法 "restore execution context" ,一旦您拥有所需的堆栈,就可以使用原始代码重新定义它们。
例如:假设您要恢复一个堆栈,其中只有 A 调用 B,B 调用 C。
A加载时,改代码直接调用B,B一加载,重新定义直接调用C;调用最顶层的方法(A);一旦调用 C(现在应该很快),将 A 和 B 重新定义为它们的原始代码。
如果涉及多个线程并且必须恢复参数值,它会变得稍微复杂一些,但仍然可以使用 JVMTI。然而,这将值得另一个问题 ;-)。
希望这可以帮助。如果您需要澄清任何问题,请随时与我联系或发表评论。
编辑:
虽然我认为这是可行的,但我也认为这是很多 (!!!) 的工作,尤其是当您想要恢复参数、局部变量和调用上下文(如指针、持有锁等)时。
按要求编辑:假设堆栈与上面相同(A 调用 B 调用 C)。尽管 A、B 和 C 内部有任意代码,但只需像这样重新定义它们:
void A() { B(); } void B() { C(); } void C() { 重新定义(); }
到达重新定义方法后,立即使用原始代码重新定义所有 类。然后你就有了你想要的堆栈。
我的问题是:是否可以(以任何方式)在运行时分析和修改调用堆栈(帧内容和堆栈内容)?
我正在寻找任何可能性 - 低级、不安全或内部 API、编写 C 扩展的可能性等。唯一的限制:它应该在标准运行时可用,无需调试或分析模式.这是我研究的重点 "is it possible at all?",而不是 "is it good idea?"。
我想从帧中收集所有本地数据,将其存储在某处,然后从堆栈中删除该帧,并可能在以后恢复它。实际上,这为我们提供了 JVM 的延续,它们将允许快速异步框架(如 python 中的 gevents)和生成器构造(如 python 中的那些)出现。
这看起来像是重复的问题,但我只找到了用 "use Thread.currentThread().getStackTrace()
" 或 "that should be done with debugging tools" 回答的问题。有similiar question to mine, but it was only answered in context of what asking guy wanted to do (work on async computations), while I need more general (java-stack oriented) answer. This question也是类似的,但是和以前一样,它专注于并行化,答案也专注于此。
我再说一遍:这是提出新语言功能提案过程中的研究步骤。我不想冒险破坏 JVM 中的任何东西 - 我正在寻找可能性,然后我将分析可能的风险并寻找它们。我知道手动操作堆栈是丑陋的,但是使用省略构造函数创建实例也是如此 - 它是 objenesis 的基础。肮脏的技巧可能很肮脏,但它们可能有助于介绍一些很酷的东西。
PS。我知道 Quasar and Lightwolf 存在,但是,如上所述,这些都是以并发为中心的框架。
编辑
一点说明:我正在寻找与未来的 JVM 和库版本兼容的东西。最好我们谈论的是被认为是稳定的东西 public API,但是如果解决方案在于内部的东西,但几乎是标准的或者在成为内部之后成为标准(比如 sun.misc.Unsafe) - 那也会做。如果仅使用 C JVM API 可以通过 C 扩展实现 - 没关系。如果这对于字节码操作是可行的——那也没关系(我认为这可能通过 ASM 实现)。
在此工具中不确定,但您可以查看 http://en.wikipedia.org/wiki/GNU_Debugger。
GDB 提供了广泛的工具来跟踪和更改计算机程序的执行。用户可以监视和修改程序内部变量的值,甚至可以独立于程序的正常行为调用函数。
我认为有一种方法可以使用 JVMTI 实现您想要的。
虽然你不能直接做你想做的事(如上面的评论所述),但你可以 instrument/redefine 方法(或整个 类)在 运行 时间。因此,您可以定义每个方法以直接调用另一个方法 "restore execution context" ,一旦您拥有所需的堆栈,就可以使用原始代码重新定义它们。
例如:假设您要恢复一个堆栈,其中只有 A 调用 B,B 调用 C。 A加载时,改代码直接调用B,B一加载,重新定义直接调用C;调用最顶层的方法(A);一旦调用 C(现在应该很快),将 A 和 B 重新定义为它们的原始代码。
如果涉及多个线程并且必须恢复参数值,它会变得稍微复杂一些,但仍然可以使用 JVMTI。然而,这将值得另一个问题 ;-)。
希望这可以帮助。如果您需要澄清任何问题,请随时与我联系或发表评论。
编辑: 虽然我认为这是可行的,但我也认为这是很多 (!!!) 的工作,尤其是当您想要恢复参数、局部变量和调用上下文(如指针、持有锁等)时。
按要求编辑:假设堆栈与上面相同(A 调用 B 调用 C)。尽管 A、B 和 C 内部有任意代码,但只需像这样重新定义它们:
void A() { B(); } void B() { C(); } void C() { 重新定义(); }
到达重新定义方法后,立即使用原始代码重新定义所有 类。然后你就有了你想要的堆栈。