【问题标题】:Memory not fully freed内存未完全释放
【发布时间】:2016-09-17 16:04:08
【问题描述】:

我刚开始第一次使用 SceneKit 和 SpriteKit 以及 ARC 创建应用程序。我注意到当我在不同的视图之间切换时,内存使用量正在迅速增加。我的第一个想法是我有内存泄漏,但我现在不确定。该行为甚至出现在这个基本示例中:

for(int r=0;r<9999999;r+=1){
    NSString *s=[NSString stringWithFormat:@"test%i",r];
    s=nil;
}

据我了解,在此循环中创建并直接释放 NSString 对象。我已经在 iPhone-Simulator 和 iPhone 上尝试过这个示例,它使应用程序在执行此循环后使用数百 MB 的 RAM。 (我正在使用 Xcode 调试导航器检查内存使用情况)

我显然误解了一些东西。为什么这个例子之后仍然保留内存?

编辑:

您也可以创建一个新项目:iOS -> 游戏 -> 游戏技术:SceneKit

然后将其添加到 viewDidLoad 中:

for(int r=0;r<999999;r+=1){
    SCNNode *tn=[SCNNode node];
    tn=nil;
}

内存将在 550MB 达到峰值,然后降至 300MB,如果有对象被完全释放并从 RAM 中删除,这将是太多了。

【问题讨论】:

  • 顺便说一句,您可以尝试模拟内存压力。 Cocoa 对象会进行各种缓存,其中一些会在出现内存警告时被清除。此外,如果您在方案中打开了任何内存调试选项(尤其是僵尸程序),请注意这些会导致额外的内存增长,并且您可能希望在分析泄漏、废弃或缓存的内存之前将其关闭。
  • 感谢您帮助我解决这个问题。我没有打开僵尸,但你的建议让我关闭了所有调试设置,这让问题消失了。所以这个问题都和调试特性有关。
  • 优秀。我已将其纳入下面的答案中。
  • '模拟内存警告'在这种情况下没有帮助,在我尝试的示例中它只释放了 1MB 或更少。
  • 即使是简单的示例,也可能存在自动释放池。可能不在这里,但您可能会感到惊讶。

标签: objective-c automatic-ref-counting


【解决方案1】:
  1. 不要依赖NSString 进行内存诊断。它具有相当非典型的行为。

    这是一个不常见的场景,我在 S.O. 上看到过。不止一次,为了将一些复杂的内存问题简化为更简单的问题,开发人员使用NSString 创建了一个简化示例,没有意识到选择该特定类会引入奇怪的、不相关的行为。新的“调试内存图”工具或久经考验的旧仪器(下文讨论)是诊断代码中潜在问题的最佳方法。

  2. 顺便说一句,您谈到立即释放对象。如果您的方法不是以alloc、new、copy 或mutableCopy 开头,则返回的对象将不会在超出范围后立即释放,因为它们是@987654331 @对象。在自动释放池耗尽之前它们不会被释放(例如,您返回到运行循环)。

    因此,如果您的应用的“高水位”标记太高,但内存最终会回落到可接受的水平,那么请考虑选择自动释放对象(和/或引入您自己的自动释放池)。但通常这种 autorelease 与 non-autorelease 对象的区别在一定程度上是学术性的,除非您有一个非常长的运行循环,在该循环中您在返回到运行循环之前分配了许多对象。

    简而言之,自动释放对象不会影响对象是否被释放,而只会影响它们何时被释放。我只是为了回应大的for 循环以及应该立即释放对象的争论而提到这一点。释放对象的精确时间会受到自动释放对象的影响。

  3. 关于您的应用程序中内存的快速增加,它可能与您在此处的示例完全无关。诊断此问题的方法是使用 Instruments(如 WWDC 2013 Fixing Memory Issues 中所述)。简而言之,选择“产品”-“配置文件”并选择“泄漏”工具(该工具也将抓取必要的“分配”工具),运行应用程序,然后准确查看已分配和未发布的内容。

    此外,Xcode 8 的“调试对象图”工具也非常有用,而且更易于使用。它在 WWDC 2016 的 Visual Debugging with Xcode 中有描述。使用此工具,您可以在左侧面板中查看对象列表,当您选择其中一个时,您可以看到与该对象关联的对象图,因此您可以诊断出您可能仍有哪些未解析的引用:

  4. 顺便说一句,您可以尝试模拟内存警告。 Cocoa 对象会进行各种缓存,其中一些会在存在内存压力时被清除。

  5. 如果您在方案中打开了任何内存调试选项(例如僵尸程序),请注意这些会导致额外的内存增长,因为它会捕获相关的调试信息。在分析泄漏、废弃或缓存的内存之前,您可能需要关闭所有调试选项。

底线,如果您看到每次迭代增长了几 kb,并且您实例化的对象都没有出现,并且您没有打开任何调试选项,那么您可能不需要担心它。许多 Cocoa 对象正在执行我们无法控制的各种缓存,通常可以忽略不计。但是,如果内存在每次迭代中都以 mb 或 gb 增长(并且不必担心第一次迭代,而只担心后续迭代),那么您确实需要仔细查看。

【讨论】:

  • 1. NSString 只是为了简单起见而选择的一个示例。在我的应用程序中,我最初与我的自定义 SceneKit 视图相同。该示例与此行具有相同的结果:SCNScene *s=[SCNScene scene];
  • 2.那可能就是我一直在寻找的。我会进一步研究这个话题。
  • 3.我已经在使用这些工具,它们使查找未发布的对象变得非常容易。这就是我对这个短循环示例的观察。然而,那些 NSStrings 或 SCNScenes 并没有在那里列出,它们仍然需要一些内存。请注意,我的示例完全是人为的。循环在那里,所以它增加了大量的内存。
  • 我并没有真正遇到问题,但我注意到在视图之间来回切换时内存使用量正在增加。我知道这听起来像是有泄漏,但我的项目仍然几乎是空的。我想在项目变得更复杂之前解决或理解这个问题。您可以将此示例循环与 NSString 或 SCNScene 添加到一个空的新项目中,它仍然会占用内存,而应该全部释放。这就是我想了解的。
猜你喜欢
  • 1970-01-01
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多