内存未完全释放

Memory not fully freed

我刚刚开始第一次使用 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 中删除,这将是太多了。

  1. 不要依赖 NSString 进行内存诊断。它有相当不典型的行为。

    这种情况并不少见,我在 S.O 上看到过。不止一次,为了将一些复杂的内存问题简化为更简单的问题,开发人员使用 NSString 创建了一个简化的示例,没有意识到选择特定的 class 会引入奇怪的、不相关的行为。新的 "Debug Memory Graph" 工具或久经考验的旧 Instruments(下面讨论)是诊断代码中潜在问题的最佳方法。

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

    所以,如果你的应用程序的 "high water" 标记太高,但内存最终回落到可接受的水平,然后考虑自动释放对象的选择(and/or 引入你自己的自动释放池) .但通常这种自动释放与非自动释放对象的区别有点学术性,除非你有一个很长的 运行ning 循环,在这个循环中你在返回到 运行 循环之前分配了许多对象。

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

  3. 关于您的应用内存快速增加,这很可能与您在此处的示例完全无关。诊断此问题的方法是使用 Instruments(如 WWDC 2013 Fixing Memory Issues 中所述)。简而言之,选择 "Product" - "Profile" 并选择 "Leaks" 工具(这将获取基本的 "Allocations" 工具,以及),运行应用程序,然后精确查看已分配但未释放的内容。

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

  4. 对了,你可以试试模拟内存警告。 Cocoa 对象进行各种缓存,其中一些在内存压力时被清除。

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

最重要的是,如果您看到每次迭代增长了几个 kb,并且 none 您实例化的对象正在显示并且您没有打开任何调试选项,那么您可能不需要担心。许多 Cocoa 对象正在进行我们无法控制的各种缓存,通常可以忽略不计。但是,如果内存在每次迭代中都以 mb 或 gb 的速度增长(并且不必担心第一次迭代,而只需担心后续迭代),那么这就是您真正需要仔细研究的问题。