【问题标题】:JVM: is it possible to manipulate frame stack?JVM:可以操作帧堆栈吗?
【发布时间】:2014-12-01 14:51:02
【问题描述】:

假设我需要在同一个线程中执行 N 个任务。这些任务有时可能需要来自外部存储的一些值。我事先不知道哪个任务可能需要这样的值以及何时。一次性获取 M 值比在 M 查询中获取相同的 M 值到外部存储要快得多。

请注意,我不能指望任务本身的合作,它们只能被视为 java.lang.Runnable 对象

现在,在我看来,理想的程序应该是这样的

  1. 循环执行所有任务。如果任务请求外部值,请记住这一点,暂停任务并切换到下一个。
  2. 一次性获取上一步请求的值。
  3. 删除所有已完成的任务(暂停的不计为已完成)。
  4. 如果还有任务,请转到步骤 1,但不是执行任务,而是从挂起状态继续执行它。

据我所知,“暂停”和“恢复”某事的唯一方法是从 JVM 堆栈中删除其相关帧,将它们存储在某处,然后将它们推回堆栈并让 JVM 继续。

是否有任何标准(不涉及比 JVM 字节码更低级别的黑客攻击)方法来做到这一点?

或者您能否建议另一种可能的方式来实现这一点(除了启动 N 个线程或使任务以某种方式协作)?

【问题讨论】:

    标签: java jvm quasar


    【解决方案1】:

    可以使用quasar 之类的东西,通过代理进行堆栈切片。任务的某种程度的合作是有帮助的,但可以使用 AOP 从外部插入暂停点。

    (IMO 最好明确说明正在发生的事情(例如使用FutureForkJoinPool)。如果一些纯代码在一个线程上运行一段时间,然后“神奇地”挂起并跳转到另一个线程,这对于调试或推理可能非常混乱。使用现代语言和库,明确异步边界的开销不应该是压倒性的。如果您的任务是根据泛型类型编写的,那么通过类似的东西相当容易scalaz Future。但这不符合您给定的要求)。

    【讨论】:

    • 谢谢,一定会看看这个。
    • 看起来它需要 Java 7。我们的项目目前使用 6,所以我将其搁置到稍后。
    • 不幸的是,这似乎不能满足我的限制,因为我基本上需要将每个方法标记为可以挂起的每个方法,它们可能是抛出异常或带有注释。
    • 您看到“自动检测”部分了吗?听起来这可以通过使用 AOP 来解决(尽管它被标记为实验性的)。
    • 是的,但这有点意味着一半的程序可能会被检测,假设实验功能甚至没有出错。在我的情况下,我不会将光纤用于并行性,而是专门用于暂停和稍后恢复的能力。任务真的在这里可以是任何东西,包括跨越多个类的数百个方法的大型任务。
    【解决方案2】:

    如前所述,Quasar 正是这样做的(它通常在 M 个线程上调度 N 个纤程,但您可以将 M 设置为 1),使用字节码转换。它甚至为每个任务(也称为“光纤”)提供了自己的堆栈跟踪,因此您可以转储它并获得完整的堆栈跟踪,而不会受到任何其他共享线程的任务的干扰。

    【讨论】:

      【解决方案3】:

      你可以试试这个

      你需要

      1. 一种保存任务当前状态的机制,因为当任务返回时,它的帧将从调用堆栈中弹出。根据返回值或类似的东西,您可以确定天气是否完成,因为您需要从它离开的点重新执行它,因此您需要保留状态信息。

      2. 为每个任务创建一个请求数据结构。当一个任务想要请求某些东西时,它会记录在那里,数据结构应该支持一个任务可以发出的所有可能的请求。

      3. 将这些 DS 存储在地图中。在循环结束时,您可以查询此 DS 以确定每个任务所需的资源类型。

      4. 获取资源,将其放入 DS 中。从返回时的状态开始任务。

      5. 任务查询DS获取资源。

      6. 当任务想要使用外部资源时,它应该使用这个 DS。

      您需要特别考虑设计请求资源的方法,因为当您再次重新执行任务时,您需要自己调用此方法,以便任务可以从它离开的地方执行。

      *DS -> 数据结构

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-01
        • 2011-09-14
        • 2019-05-09
        • 2014-10-23
        • 1970-01-01
        • 2019-09-19
        • 2011-09-10
        • 2015-12-17
        相关资源
        最近更新 更多