在递归调用Future [T]中的函数时,如何减轻内存泄漏?

时间:2015-05-04 21:01:26

标签: scala recursion concurrency playframework tail-recursion

这个问题的灵感来自早期Stackoverflow 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](...)方法返回Future[Iteratee[Array[Byte],A]]。上述文章的作者包括了" scala.concurrent Futures未能合并的评论"一旦他们回来,但Twitter的未来有些如何管理这个。我使用的是PlayFramework实现,它使用标准Scala 2.1X库提供的期货。

您是否有证据支持或驳回这些索赔?我的代码是否会造成内存泄漏?

2 个答案:

答案 0 :(得分:1)

您链接的问题(在评论中,而不是问题或答案)是指flatMap发生的内存泄漏。您的代码没有使用flatMap,所以我不认为您不会遇到任何问题,因为您只是通过一个条件执行另一个Future回调,而不是关心前一个。

无论哪种方式,它都是无关紧要的,因为这个问题已在2.10.3中修复。请参阅问题SI-7336和修复此问题的pull request以获取更多信息。

答案 1 :(得分:1)

虽然您的调用不会受到常规递归调用的堆栈溢出危险的影响,但它也不是尾递归的。 connect()的退出语句是onComplete,只允许connect()退出并释放其堆栈。对connect()的“递归”调用实际上是在未来完成时作为延续,因此不是实际的递归。

警告:如果get实际上总是返回Future.successful(可能在单元测试中),那么get可能会受到堆栈的影响溢出,因为Success案例会在onComplete退出之前立即触发。我没有对此进行测试,根据ExecutionContext的属性,它可能会发生或可能不会发生。