【问题标题】:NSURLSession - High Memory UsageNSURLSession - 高内存使用
【发布时间】:2015-10-31 06:52:34
【问题描述】:

在我的应用程序(一个 OS X 应用程序)中,我在短时间内发送了大约 5,000 个 URL 请求,以从服务器检索 JSON 对象。我同时发送 50 个请求,等待所有请求完成,然后再发送一批 50 个。但是我遇到了一些内存管理问题...

当所有请求都完成时(大约 2 分钟),我的应用程序的内存使用量增加了大约 250MB,而且这个内存永远不会被释放。我正在使用NSURLSession/dataTaskWithURL 来获取数据,为了排除故障,我已经从完成处理程序中删除了所有代码——它只是触发请求并丢弃响应。这是一个例子:

func fetchData() {
    let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
    // Also tried .defaultSessionConfiguration()
    config.URLCache = nil
    config.HTTPCookieAcceptPolicy = .Never
    config.HTTPCookieStorage = nil
    let session = NSURLSession(configuration: config)

    let query = session.dataTaskWithURL(myURL) { (data, response, error) -> Void in
        session.invalidateAndCancel()
        // Normally call a completion handler here too
    }
    query.resume()
}

请注意,我将禁用所有缓存和 cookie,并在请求完成时调用 invalidateAndCancel()。我也尝试过使用标准的sharedSession(),但这些都没有任何区别 - 内存使用量总是快速攀升并永久保持在高位。

这是一个大问题,因为我的应用程序需要定期发送这些请求,而且每个周期的内存只会爬得更高——最终达到几 GB 的 RAM。还应该注意的是,一旦内存使用量达到这一点,我的程序就会变慢。

有人知道为什么NSURLSession 可能会保留这么多内存吗?使用 Instruments 进行分析并没有发现任何内存泄漏,所以我很困惑。任何帮助或建议将不胜感激。

【问题讨论】:

  • 关于为什么需要 5000 个会话的更多信息?听起来你在这里与 iOS 对抗,这是一个疯狂的高数字?
  • @Ben 这是一个 Mac 应用程序,而不是 iOS 应用程序。基本上,它 24/7 全天候运行并对大量数据进行分析。这是很多请求,但机器自然有互联网连接来处理它。我还应该提到,我尝试重用会话而不是为每个请求创建一个新会话,但无济于事。

标签: ios swift macos memory nsurlsession


【解决方案1】:

两件事:

  1. 由于您使用的是NSURLSession,请确保您没有为这 5000 个请求中的每一个都实例化一个新请求。实例化一个然后重用它。也许尝试改用NSURLSession.sharedSession()

    我看到每个会话的内存消耗适中,如果您有大量会话,它可以加起来

  2. 您说您运行 Instruments 是为了寻找泄漏。如今,您很少会发现像这样令人震惊的事情。但是您还应该通过分析两个时间点之间的分配或生成来寻找强大的参考周期等,并确定 什么 占 250mb,然后查看它的分配位置等。

    查看 WWDC 视频,例如 2013 年的 Fixing Memory Issues 或 2012 年的 iOS App Performance: Memory(并忽略标题中包含 iOS 的事实;因为其中许多原则同样适用于 MacOS),这将说明如何诊断此类问题.

【讨论】:

  • 我已经缩小了问题的范围。如果我尝试同时发送多个请求,它会很快耗尽内存。例如,我会同时发送 50 个请求,等待所有 50 个请求完成,然后发送下一批。这导致永远不会释放的高内存使用率。但是一次只发送一个请求,连续发送 5000 个请求,不会占用太多内存。问题是,它需要更长的时间才能完成。知道为什么并发请求会占用内存吗?我发现创建一个会话(不使用 sharedSession)并重用它可以得到最好的结果,就像你建议的那样。
  • 运行并发请求时(无论如何我一次不会超过 4 或 5 个),您的峰值内存使用量应该会增加,但是当这些请求完成时应该释放它。当您查看未释放的分配时,您发现了什么?
  • 我认为我需要对 Instruments 的使用做更多的研究,因为我不完全确定如何解释我在 Allocations 中看到的内容。我当然希望在发出请求时内存使用量会激增,但奇怪的是它们在完成后没有被释放。
  • 我上面引用的 WWDC 视频是很好的实用介绍。它们包括使用 Instruments 追踪问题的良好演示。
【解决方案2】:

你试过设置吗

Config = nil
Query = nil
Session = nil
[session finishTasksAndInvalidate]

最后?但就我个人而言,我更关心你为什么需要提出 5000 个请求?对我来说似乎是糟糕的设计......

【讨论】:

  • 我试了一下,除了查询,因为它不能在自己的完成处理程序中设置为零。但它仍然保留了相同数量的内存。作为对设计的回应,我也会在这里为未来的读者提及这一点:这是一个 24/7 全天候运行的 Mac 应用程序,收集大量数据并对这些数据执行不同类型的分析。数据来自多个来源,并非所有来源都支持批量获取,因此(不幸的是)5000 个请求是完全必要的。
  • 您是否尝试在 query.resume 之后而不是在块内将它们设置为 nil?
  • 是的,我也尝试在 query.resume() 之后将它们设置为 nil - 没有区别。
  • 其中包括:[session finishTasksAndInvalidate
猜你喜欢
  • 2015-07-18
  • 2020-11-09
  • 2015-11-01
  • 2017-01-17
  • 1970-01-01
  • 2011-11-06
  • 2016-01-24
  • 2023-03-13
  • 2015-06-01
相关资源
最近更新 更多