理解iPhone上的内存消耗

35

我正在使用OpenGL ES开发2D iPhone游戏,但不断遇到24MB内存限制的问题——我的应用程序经常崩溃,显示错误代码101。我努力查找内存消耗的位置,但Instruments中的数字仍然比我预期得到的要大得多。

我使用Memory Monitor、Object Alloc、Leaks和OpenGL ES工具来运行应用程序。当应用程序被加载时,可用物理内存从37 MB降至23 MB,在Object Alloc中分配的内存大约是7 MB,在Leaks中显示有两个或三个几个字节大小的泄漏,Gart对象大小约为5 MB,而Memory Monitor则显示应用程序占用了大约14 MB的实际内存。我感到困惑的是内存去哪了——当我深入了解对象分配时,大部分内存都在纹理中,这正如我所预期的那样。但是我的纹理分配计数器和Gart对象大小都表示纹理应该占用大约5MB,我不知道其他地方还有哪些内存消耗。

我不知道是否还有其他值得一提的内存分配,Object Alloc也没有显示。内存到哪里去了?(如果这还不够,请告诉我更多细节)


更新:我尽力找到可能分配如此多内存的地方,但没有结果。让我疯狂的是Object Allocations(约7MB)和Memory Monitor显示的实际内存使用情况之间的差异(约14MB)。即使有很多泄漏或我忘记的巨大内存块,它们应该仍会在Object Allocations中显示出来,不是吗?

我已经尝试过通常的怀疑对象,比如UIImage及其缓存,但没有帮助。有没有一种类似“调试器”的方式来跟踪内存使用情况,逐行查看每个语句对内存使用情况的影响?


我目前所发现的情况:

  1. 我确实使用了那么多内存。很难测量真正的内存消耗,但经过大量计算,我认为内存消耗确实很高。这是我的问题。

  2. 我找不到测量内存使用的简单方法。内存监视器的数字是准确的(这些才是真正重要的数字),但内存监视器不能告诉您内存具体去了哪里。对象分配工具对跟踪真实内存使用几乎没有用处。当我创建纹理时,分配的内存计数器会暂时增加(将纹理读入内存),然后下降(传递纹理数据到OpenGL,释放)。这是可以接受的,但并不总是发生——有时即使纹理已经传递给OpenGL并从“我的”内存中释放,内存使用仍然很高。这意味着对象分配工具显示的分配总内存量比真实总内存消耗小,但比真实消耗减去纹理大(真实 - 纹理 < 对象分配 < 真实)。自己琢磨吧。

  3. 我误读了编程指南。24 MB的内存限制适用于纹理和表面,而不是整个应用程序。实际的红线在更远的地方,但我找不到任何硬数据。共识是25-30 MB是上限。

  4. 当系统内存不足时,它开始发送内存警告。我几乎没有什么东西可以释放,但其他应用程序确实会将一些内存释放回系统,特别是Safari(它似乎正在缓存网站)。当内存监视器显示的可用内存为零时,系统开始终止进程。

我不得不咬紧牙关,重新编写了代码的一些部分,以更有效地使用内存,但我可能还是有压力。如果我要设计另一个游戏,我肯定会考虑一些资源分页。对于当前的游戏来说,这很困难,因为它一直在运动中,即使在另一个线程中加载纹理也会妨碍。 我非常想知道其他人如何解决这个问题。

请注意,这只是我的观点,并不一定很准确。如果我在这个话题上发现更多要说的,我会更新这个问题。我会保持这个问题开放,以便理解这个问题的人可以回答,因为所有这些方法都更像是权宜之计和猜测,而不是其他任何东西。
5个回答

11

我非常怀疑这是Instruments中的一个错误。

首先,阅读Jeff Lamarche关于openGL纹理的博客文章

  • 提供了一个简单的示例,说明如何加载纹理而不会导致泄漏
  • 让人们了解到“小”图像在加载到openGL后实际上使用了“大量”的内存

摘录:

纹理,即使它们由压缩图像制成,也会使用大量应用程序的内存堆,因为它们必须在内存中扩展以便使用。每个像素占用四个字节,因此忘记释放纹理图像数据可能会迅速消耗您的内存。

其次,可以使用Instruments调试纹理内存。有两个分析配置:OpenGL ES AnalyzerOpenGL ES Driver。您需要在设备上运行它们,因为模拟器不使用OpenGL。只需选择Product->Profile from XCode,然后在Instruments启动时查找这些配置文件即可。


掌握这些知识后,我会这样做:

  • 检查您是否存在内存泄漏--这显然会导致此问题。
  • 确保您没有访问已释放的内存--这是崩溃的常见原因。
  • 创建一个单独的测试应用程序,并尝试单独加载纹理(以及组合加载)以找出哪个纹理(或其组合)导致问题。

更新:在考虑了您的问题后,我一直在阅读苹果的OpenGL ES编程指南,它有非常好的信息。强烈推荐!


文章的链接已经失效了,你有什么想法在哪里可以找到这些信息吗? - Jonas Sourlier
我添加了一些有关文章内容的信息。我还删除了死链接。 - bentford
我正在查看OpenGL ES分析器和OpenGL ES驱动程序配置,但是我找不到已使用的纹理内存量。如何获取iOS应用程序实际使用的OpenGL纹理内存? - Ricardo Sanchez-Saez

3

一种方法是注释掉代码并检查错误是否仍然存在。虽然这很琐碎和初级,但如果你知道错误出在哪里,它可能会有所帮助。

为什么它崩溃的地方就是它崩溃的原因等等。


很遗憾,这样做行不通,我不能简单地注释掉一个分配并希望应用程序不会注意到。 - zoul
实际上,这并不是一个坏建议。如果应用程序消耗越来越多的内存,可以注释掉部分内存分配代码,并将潜在嫌疑对象缩小到单个类或行。 - zoul

2

嗯,这些细节不够多,但如果Leaks没有显示泄漏的位置,那么有两个重要的选项:

[i] Leaks错过了一个泄漏 [ii] 实际上并没有内存泄漏

修复 [i] 非常困难,但正如Eric Albert所说,向Apple提交错误报告将会有所帮助。[ii] 意味着您正在使用的内存仍然可以在某个地方访问,但也许您已经忘记了它。任何列表是否在增长,而没有丢弃旧条目?任何缓冲区是否被频繁重新分配(realloc())?


任何封装在GCD调用的大括号内的代码都会被屏蔽掉两件事情:错误报告和有时的分配计数。这通常只适用于CoreFoundation或任何其他非UIKit或非NSFoundation的东西。 - James Bush

2

对于那些在2012年之后看到这篇文章的人:

实际加载到设备物理内存中的内存是VM Tracker工具中的Resident Memory。

Allocation Instrument只标记了由malloc/[NSObject alloc]和一些框架缓冲区创建的内存,例如,解压缩的图像位图不包括在Allocation Instrument中,但它总是占用大部分内存。

请观看苹果公司WWDC 2012会议242 iOS应用程序性能:内存以获取更多信息。


0

这并没有直接帮助到您,但如果您发现内存工具无法提供您所需的所有数据,请在bugreport.apple.com上提交错误报告。附上您的应用程序副本以及说明工具如何无法满足您的分析要求,Apple将会尽力改进这些工具。谢谢!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接