在 Future[T] 中递归调用函数时如何缓解内存泄漏?
How do I mitigate memory leaks when recursively calling a function inside a Future[T]?
这个问题的灵感来自早期 Whosebug article 中关于同一主题的一些评论,也受到我正在编写的一些代码的启发。鉴于其中包含的示例,我有点相信这种模式是尾递归的。如果是这种情况,我如何减轻累积期货所造成的内存泄漏,这些期货的底层线程从不加入它们从中产生的 ForkJoinPool?
import com.ning.http.client.AsyncHttpClientConfig.Builder
import play.api.libs.iteratee.Iteratee
import play.api.libs.iteratee.Execution.Implicits.defaultExecutionContext
import play.api.libs.ws.ning.NingWSClient
import scala.util.{Success,Failure}
object Client {
val client = new NingWSClient(new Builder().build())
def print = Iteratee.foreach { chunk: Array[Byte] => println(new String(chunk)) }
def main(args: Array[String]) {
connect()
def connect(): Unit = {
val consumer = client.url("http://streaming.resource.com")
consumer.get(_ => print).onComplete {
case Success(s) => println("Success")
case Failure(f) => println("Recursive retry"); connect()
}
}
}
}
在我分享的示例中,get[A](...)
方法 return 是 Future[Iteratee[Array[Byte],A]]
。上面文章的作者我已经发表评论说 "scala.concurrent Futures don't get merged" 一旦他们 return 但是 Twitter 的未来有些如何管理这个。但是,我使用的是 PlayFramework 实现,它使用标准 Scala 2.1X 库提供的期货。
你们中有人有证据支持或驳回这些说法吗?我的代码会造成内存泄漏吗?
您链接的问题(在评论中,而不是在问题或答案中)指的是 flatMap
发生的内存泄漏。您的代码不使用 flatMap
,所以我认为您不会 运行 遇到任何问题,因为您只是通过回调有条件地执行另一个 Future
,而不是完全不在乎以前的。
无论哪种方式,这都无关紧要,因为此问题已在 2.10.3 中修复。有关详细信息,请参阅修复它的问题 SI-7336 and the pull request。
虽然您的调用不会遭受常规递归调用的堆栈溢出危险,但它也不是尾递归。 connect()
的退出语句是 onComplete
,它只是让 connect()
退出并释放其堆栈。 "recursive" 对 connect()
的调用实际上是未来完成时的延续,因此不是实际的递归。
警告:如果 get
实际上总是返回 Future.successful
(就像在单元测试中一样),很可能 get
可能会发生堆栈溢出,因为 Success
案例希望在 onComplete
退出之前立即触发。我还没有对此进行测试,这可能会发生也可能不会发生,具体取决于 ExecutionContext
的属性。
这个问题的灵感来自早期 Whosebug article 中关于同一主题的一些评论,也受到我正在编写的一些代码的启发。鉴于其中包含的示例,我有点相信这种模式是尾递归的。如果是这种情况,我如何减轻累积期货所造成的内存泄漏,这些期货的底层线程从不加入它们从中产生的 ForkJoinPool?
import com.ning.http.client.AsyncHttpClientConfig.Builder
import play.api.libs.iteratee.Iteratee
import play.api.libs.iteratee.Execution.Implicits.defaultExecutionContext
import play.api.libs.ws.ning.NingWSClient
import scala.util.{Success,Failure}
object Client {
val client = new NingWSClient(new Builder().build())
def print = Iteratee.foreach { chunk: Array[Byte] => println(new String(chunk)) }
def main(args: Array[String]) {
connect()
def connect(): Unit = {
val consumer = client.url("http://streaming.resource.com")
consumer.get(_ => print).onComplete {
case Success(s) => println("Success")
case Failure(f) => println("Recursive retry"); connect()
}
}
}
}
在我分享的示例中,get[A](...)
方法 return 是 Future[Iteratee[Array[Byte],A]]
。上面文章的作者我已经发表评论说 "scala.concurrent Futures don't get merged" 一旦他们 return 但是 Twitter 的未来有些如何管理这个。但是,我使用的是 PlayFramework 实现,它使用标准 Scala 2.1X 库提供的期货。
你们中有人有证据支持或驳回这些说法吗?我的代码会造成内存泄漏吗?
您链接的问题(在评论中,而不是在问题或答案中)指的是 flatMap
发生的内存泄漏。您的代码不使用 flatMap
,所以我认为您不会 运行 遇到任何问题,因为您只是通过回调有条件地执行另一个 Future
,而不是完全不在乎以前的。
无论哪种方式,这都无关紧要,因为此问题已在 2.10.3 中修复。有关详细信息,请参阅修复它的问题 SI-7336 and the pull request。
虽然您的调用不会遭受常规递归调用的堆栈溢出危险,但它也不是尾递归。 connect()
的退出语句是 onComplete
,它只是让 connect()
退出并释放其堆栈。 "recursive" 对 connect()
的调用实际上是未来完成时的延续,因此不是实际的递归。
警告:如果 get
实际上总是返回 Future.successful
(就像在单元测试中一样),很可能 get
可能会发生堆栈溢出,因为 Success
案例希望在 onComplete
退出之前立即触发。我还没有对此进行测试,这可能会发生也可能不会发生,具体取决于 ExecutionContext
的属性。