promise链接在Scala中是如何工作的?为什么有必要?

promise链接在Scala中是如何工作的?为什么有必要?,scala,promise,garbage-collection,future,Scala,Promise,Garbage Collection,Future,我找到了一些关于Scala未来和承诺的文章和源代码: 当前的实施情况,带有一条解释承诺链接的重要注释: 压力测试导致记忆耗尽,但没有承诺: 但我仍然不太明白承诺链接到底是如何工作的。 首先,内存耗尽压力测试引用回调中的大数组。 这些应该会导致大数组留在内存中,直到回调被执行为止 我试着用伪代码编写一个压力测试场景 显示i=3的呼叫的示例: x4 = call3 call3 = { val array3 = new BigArray val f3 = Future { 3

我找到了一些关于Scala未来和承诺的文章和源代码:

  • 当前的实施情况,带有一条解释承诺链接的重要注释:
  • 压力测试导致记忆耗尽,但没有承诺:
但我仍然不太明白承诺链接到底是如何工作的。 首先,内存耗尽压力测试引用回调中的大数组。 这些应该会导致大数组留在内存中,直到回调被执行为止

我试着用伪代码编写一个压力测试场景

显示i=3的呼叫的示例:

x4 = call3

call3 = {
    val array3 = new BigArray
    val f3 = Future { 3 }
    x3 = f3.flatMap( 3 => array3; call2 )

    return x3
}

call2 = {
    val array2 = new BigArray
    val f2 = Future { 2 }

    x2 = f2.flatMap( 2 => array2; call1 )

    return x2
}

call1 = {
    val array1 = new BigArray
    val f1 = Future { 1 }

    x1 = f1.flatMap( 1 => array1; call0 )

    return x1
}

call0 = {
    val array0 = new BigArray
    val f0 = Future { 0 }

    x0 = f0.flatMap( 0 => Future.succesful() )

    return x0
}
通常,当f0、f1、f2和f3完成时,会触发x0、x1、x2和x3,然后调用它们的函数,例如:

1 => array1; call0
所以它会调用
x1.completeWith(call0)
,基本上就是
x1.completeWith(x0)

这将导致以下链条:

x4.completeWith(x3)
x3.completeWith(x2)
x2.completeWith(x1)
x1.completeWith(x0)
x0.completeWith(Future.successful())
根据我的理解,由于所有调用都会导致相同的结果,因此可以将它们链接为:

x4.completeWith(Future.successful())
只要x0、x1、x2和x3的所有回调都移动到x4。 所有的未来/承诺都是一样的吗

现在,promise链接在Scala 2.13.xx中到底是如何运行的? x4是等待其他未来完成的根吗? 其他期货/承诺现在是否转换为链接[T]? Scala 2.13.xx实现中的
linkRootOf
方法似乎创建了一个指向目标的新链接,并将其存储在未来/承诺状态。 它用它替换它的回调。 回调被移动到根未来/承诺目标,以便在根未来/承诺完成时执行? 这在f0完成时发生

即使存在指向根未来/承诺的链接链,我也不明白为什么它不再泄漏

大数组的引用在f0、f1、f2和f3完成时访问,此后执行回调。
但是现在不是创建链接的时候了吗,所以引用现在已经不存在了
completeWith
应该是无阻塞的,因此可以释放阵列,即使不创建链接?

由于还没有人回答这个问题,我将尝试自己回答。 我在Scala论坛上询问了有关承诺链接的问题,并得到了解释:

据我所知,只有当承诺/未来与我所怀疑的完全相同时,它才会起作用

垃圾收集没有释放闭包中数组引用的内存耗尽只是Scala编译器中的一个bug。因此,由于这个缺陷,整个优化过程有点过于工程化。 我无法用最新的Scala版本重现内存耗尽

但是,减少链长度将与承诺链接一起工作,因此如果承诺相同,则承诺将减少,但由于承诺不应占用那么多内存,因此优化并非真正必要。我找不到这么多嵌套
flatMap
调用的用例

根据我在问题中发布的示例的Scala FP和Twitter Util实现中所阅读的内容,链接将以以下方式生成:

x4.completeWith(x3) // x3 becomes a link to x4 and moves all of its callback to x4
x3.completeWith(x2) // x2 becomes a link to x3. Since x3 is a link to x4, all the callbacks are moved to x4. When the compression of the chain takes place it is directly linked to x4, so x3 can be released by the garbage collection since there is no reference to it anymore.
x2.completeWith(x1) // x1 becomes a link to x2 and by compression directly to x4
x1.completeWith(x0) // x0 becomes a link to x1 and by compression directly to x4
x0.completeWith(Future.successful()) // the passed future completes x0 since it is already completed and since x0 is a link to x4 it will actually complete x4 and all callbacks will be submitted to the underlying executor
链接链的压缩允许垃圾收集更早地释放链接元素,因为它们不再在任何地方引用。 这意味着你记忆中的承诺更少。 始终只有
x4
和一个指向它的链接。 回调在根promise/future
x4
中收集。 我们必须假设
x3
x2
x1
x0
trycomplewith
调用之前注册了一些回调。 否则,将不会移动任何内容