Guard Malloc导致应用程序行为异常

3

我有一个iOS应用程序,我认为发生了内存破坏。因此,我在Xcode中启用了Guard Malloc、Guard Edges和Scribble,并运行它来尝试追踪问题。但是启用Guard Malloc后,事情就开始以非常奇怪的方式出错了。在某些(可预测的)情况下,视图的背景不会绘制,你可以看到它们后面的视图。如果我进行文件下载,应用程序会在下载结束时在主线程上崩溃,堆栈上除了main之外没有我的代码,并且有时在控制台中记录一些图形错误:

<Error>: CGBitmapContextInfoCreate: unable to allocate 201000 bytes for bitmap data
<Error>: CGContextSetInterpolationQuality: invalid context 0x0

这些东西中有一些(例如CGBitmapContextInfoCreate)在我的代码中没有使用。

最后记录的事情类似于:

Failed to VM allocate 262144 bytes
Explicitly trapping into debugger!!!

有没有其他人遇到过Guard Malloc导致这些错误?这可能是什么问题的根源?


1
你是在设备上进行测试吗? - Tommy
1
@Tommy - 不,这是在模拟器中。据我所知,在设备上无法使用Guard Malloc。 - Tom Hamming
1个回答

4
Guard Malloc通过内存管理单元(MMU)工作。 MMU允许您将某些内存部分标记为进程可以访问的,将某些区域标记为非法 - 这就是所谓的受保护内存的本质。它们通过将内存划分为单个页面来实现这一点,这些页面通常为4kb,并为每个页面分配相关权限。它们无法针对每个地址单独存储属性,因为那将需要大量的存储空间。
正常运行代码时,MMU会引发非法访问异常。这意味着某些越界访问不会引发异常,因为大多数数据都小于4kb,因此大部分数据与其他数据共享页面。即使不是您打算写入的数组或页面已被新对象重用,也可以在页面的任何位置进行写入。
因此,Guard Malloc为每个对象提供单独的页面。这会极大地增加您的内存占用量,因为它将所有对象大小舍入到页面大小。这也是导致性能下降的原因 - 任何合理的缓存算法都无法正确运行。
副作用是可用存储空间更少。假设您有一个通常占用280字节存储空间的NSString。现在它占用了整个页面。因此,您很快就会用完内存。 (编辑:根据Greg下面的评论,一旦分配了页面,Guard Malloc不允许将其返回到池中,因此您的内存占用量变成累积性的,这意味着您几乎肯定最终会耗尽内存,因为[几乎]所有对象都放在堆上,并且返回对象是正常的)
Guard Edges可能会加剧这种情况,因为它在每个分配之间放置空页面。因此,每个分配的对象都会从虚拟内存池中至少取出一个额外页面,否则它不会。如果虚拟池与物理池的大小足够接近,则可能会影响您。
CGBitmapContextInfoCreate几乎肯定由您使用的某个标准UIView的内部使用,当被要求更新其内容时。系统没有足够的内存来满足请求,而且看起来视图没有试图优雅地处理该结果。
可能唯一有意义的建议是单独尝试调试工具。

1
我不相信守卫边缘页面会占用物理内存。它们确实会占用地址空间,反过来会消耗内核中的资源,但是一个既不可读也不可写的页面并不需要后备存储器,虚拟内存子系统足够聪明,不会给它分配任何资源。 - bbum
1
@bbum 你可能是对的;他们在虚拟地址空间中切割了一些空间——因此,你可以使用的 RAM 数量取决于你拥有多少 RAM 和虚拟池。做出极其安全的假设,作者拥有一个 64 位系统,与虚拟地址池相比,他们拥有的物理 RAM 很少。尽管人们会认为 MMU 当前也无法映射整个 64 位范围,因为映射的存储是有限的? - Tommy
是的 - 内核资源可能相当少,但这也很可能是一个非常次优的状态。如果我没记错,模拟器只是使用系统的虚拟内存。消耗速度大多如你所描述,但要记住,在23位进程中,你可以使用的地址空间远远少于4GB。我想远远少于2GB。 - bbum
Guard Malloc 目前的另一个问题是它从不释放任何内存。每个应用程序在运行足够长时间后都会因为 Guard Malloc 而耗尽空间。 - Greg Parker
这可能或可能不是正在运行的代码架构的指示。尝试在为模拟器编译的可执行文件上执行“arch”。 - bbum
显示剩余2条评论

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