内存未完全释放
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 中删除,这将是太多了。
不要依赖 NSString
进行内存诊断。它有相当不典型的行为。
这种情况并不少见,我在 S.O 上看到过。不止一次,为了将一些复杂的内存问题简化为更简单的问题,开发人员使用 NSString
创建了一个简化的示例,没有意识到选择特定的 class 会引入奇怪的、不相关的行为。新的 "Debug Memory Graph" 工具或久经考验的旧 Instruments(下面讨论)是诊断代码中潜在问题的最佳方法。
顺便说一句,你说的是立即释放对象。如果您的方法不是以 alloc
、new
、copy
或 mutableCopy
开头,则返回的对象将 not 在之后立即释放超出范围,因为它们是 autorelease
个对象。在自动释放池被耗尽之前它们不会被释放(例如,你返回到 运行 循环)。
所以,如果你的应用程序的 "high water" 标记太高,但内存最终回落到可接受的水平,然后考虑自动释放对象的选择(and/or 引入你自己的自动释放池) .但通常这种自动释放与非自动释放对象的区别有点学术性,除非你有一个很长的 运行ning 循环,在这个循环中你在返回到 运行 循环之前分配了许多对象。
简而言之,autorelease 对象不会影响对象是否被释放,而只会影响它们何时被释放。我只是在回应大型 for
循环和对象应立即释放的争论时才提到这一点。释放对象的精确时间受自动释放对象的影响。
关于您的应用内存快速增加,这很可能与您在此处的示例完全无关。诊断此问题的方法是使用 Instruments(如 WWDC 2013 Fixing Memory Issues 中所述)。简而言之,选择 "Product" - "Profile" 并选择 "Leaks" 工具(这将获取基本的 "Allocations" 工具,以及),运行应用程序,然后精确查看已分配但未释放的内容。
此外,Xcode 8 的 "Debug Object Graph" 工具也非常有用,而且更易于使用。它在 WWDC 2016 的 Visual Debugging with Xcode 中有描述。使用此工具,您可以在左侧面板中看到一个对象列表,当您选择一个对象时,您可以看到与该对象关联的对象图,因此您可以诊断您可能仍有哪些未解决的引用:
对了,你可以试试模拟内存警告。 Cocoa 对象进行各种缓存,其中一些在内存压力时被清除。
如果您在您的方案中打开了任何内存调试选项(例如,僵尸),请注意这些选项会导致额外的内存增长,因为它会捕获相关的调试信息。在分析泄漏、废弃或缓存的内存之前,您可能希望关闭任何调试选项。
最重要的是,如果您看到每次迭代增长了几个 kb,并且 none 您实例化的对象正在显示并且您没有打开任何调试选项,那么您可能不需要担心。许多 Cocoa 对象正在进行我们无法控制的各种缓存,通常可以忽略不计。但是,如果内存在每次迭代中都以 mb 或 gb 的速度增长(并且不必担心第一次迭代,而只需担心后续迭代),那么这就是您真正需要仔细研究的问题。
我刚刚开始第一次使用 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 中删除,这将是太多了。
不要依赖
NSString
进行内存诊断。它有相当不典型的行为。这种情况并不少见,我在 S.O 上看到过。不止一次,为了将一些复杂的内存问题简化为更简单的问题,开发人员使用
NSString
创建了一个简化的示例,没有意识到选择特定的 class 会引入奇怪的、不相关的行为。新的 "Debug Memory Graph" 工具或久经考验的旧 Instruments(下面讨论)是诊断代码中潜在问题的最佳方法。顺便说一句,你说的是立即释放对象。如果您的方法不是以
alloc
、new
、copy
或mutableCopy
开头,则返回的对象将 not 在之后立即释放超出范围,因为它们是autorelease
个对象。在自动释放池被耗尽之前它们不会被释放(例如,你返回到 运行 循环)。所以,如果你的应用程序的 "high water" 标记太高,但内存最终回落到可接受的水平,然后考虑自动释放对象的选择(and/or 引入你自己的自动释放池) .但通常这种自动释放与非自动释放对象的区别有点学术性,除非你有一个很长的 运行ning 循环,在这个循环中你在返回到 运行 循环之前分配了许多对象。
简而言之,autorelease 对象不会影响对象是否被释放,而只会影响它们何时被释放。我只是在回应大型
for
循环和对象应立即释放的争论时才提到这一点。释放对象的精确时间受自动释放对象的影响。关于您的应用内存快速增加,这很可能与您在此处的示例完全无关。诊断此问题的方法是使用 Instruments(如 WWDC 2013 Fixing Memory Issues 中所述)。简而言之,选择 "Product" - "Profile" 并选择 "Leaks" 工具(这将获取基本的 "Allocations" 工具,以及),运行应用程序,然后精确查看已分配但未释放的内容。
此外,Xcode 8 的 "Debug Object Graph" 工具也非常有用,而且更易于使用。它在 WWDC 2016 的 Visual Debugging with Xcode 中有描述。使用此工具,您可以在左侧面板中看到一个对象列表,当您选择一个对象时,您可以看到与该对象关联的对象图,因此您可以诊断您可能仍有哪些未解决的引用:
对了,你可以试试模拟内存警告。 Cocoa 对象进行各种缓存,其中一些在内存压力时被清除。
如果您在您的方案中打开了任何内存调试选项(例如,僵尸),请注意这些选项会导致额外的内存增长,因为它会捕获相关的调试信息。在分析泄漏、废弃或缓存的内存之前,您可能希望关闭任何调试选项。
最重要的是,如果您看到每次迭代增长了几个 kb,并且 none 您实例化的对象正在显示并且您没有打开任何调试选项,那么您可能不需要担心。许多 Cocoa 对象正在进行我们无法控制的各种缓存,通常可以忽略不计。但是,如果内存在每次迭代中都以 mb 或 gb 的速度增长(并且不必担心第一次迭代,而只需担心后续迭代),那么这就是您真正需要仔细研究的问题。