可可64位二进制文​​件泄漏内存? (释放NSData不释放内存)

时间:2009-09-05 15:46:24

标签: objective-c cocoa memory-leaks 64-bit nsdata

我用我的应用程序的不同版本玩了一段时间,看起来很奇怪:

我的应用程序空闲占用空间为5mb。当保存上传文件大小的文件时。上传后,应释放保留的内存。现在构建中存在差异(gc =垃圾收集器):

  • 32bit i386 no-GC:立即释放所有内存。
  • 32bit i386 GC:几乎所有内存都立即被释放。其余的一段时间后。
  • 64bit x86_64 no-GC:释放最小内存。像10%
  • 64bit x86_64 GC:根本没有内存被释放。记忆保持数小时。 (活动星期一)

我正在使用LLVM和CLANG。我一直在运行今天的仪器,并检查泄漏/僵尸/等。一切似乎都很干净。 (该应用程序相当简单。)

有这种行为的解释吗?


更新

这是一些奇怪的东西。我把问题归结为:

我将一个20mb的文件加载到NSData中并释放它。我这样做没有启用任何垃圾收集。代码是:

NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigshit"];
[bla release];

当我为i386 32bit构建时,20mb被分配并释放。当我将构建切换到64位x86_64时,版本什么都不做。分配20mb。

upper pic 32bit lower 64 http://kttns.org/zguxn

两个应用程序之间没有区别,除了上面的一个是32位而下面的64位。没有GC运行。 (启用GC后,会出现同样的问题。)


更新2:

当我从头开始创建一个新的cocoa应用程序时,只能使用applicationDidFinishLaunching中的高级代码,可以观察到相同的行为。在64位模式下,内存不会被释放。 i386按预期工作。

NSString而不是NSData出现同样的问题。当我启动64位内核时,它也会出现。 (启动时保持64位。)

操作系统是10.6.0

3 个答案:

答案 0 :(得分:10)

首先,使用Instrument的Object Graph仪器验证内存不再被认为正在使用中;在某个地方没有保留计数或强引用。

如果它不再使用,那么内存就会因为你没有达到收藏家关心的门槛而坚持下去。

然而,这句话:

  

64位x86_64 no-GC:最小内存   释放。像10%

让我警惕。具体来说,如果您的代码设计为在非GC中工作 - 使用retain / release - 那么(a)您有内存泄漏,如果使用CFRetain或某种全局缓存,可能会影响GC或(b) )你没有使用正确的工具来判断你是否有内存泄漏。

那么,你如何确定你正在泄漏记忆?

<强>更新;您正在使用活动监视器来监视进程的RSIZE / VSIZE。除了“我的流程随着时间的推移而增长”之外,这实际上并没有告诉你任何有用的东西。

更有可能(我没有看过源代码),这段代码:

NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigpoop"];

将导致2​​0MB文件mmap()进入该进程。根本没有涉及malloc()样式分配。相反,操作系统将20MB的连续地址空间交给您的进程,并将文件的内容映射到其中。当你阅读NSData的内容时,它会在你去的时候在文件中出现页面错误。

当您释放bla时,映射将被销毁。但这并不意味着VM子系统会将应用程序的地址空间减少20MB。

所以,你正在烧掉一堆地址空间,而不是实际的内存。由于您的进程是64位,因此地址空间几乎是无限资源,使用地址的成本非常低,因此操作系统的实现方式就是这样。

即。没有泄漏,你的应用程序表现正常,GC或没有。

这是一种常见的误解,因此,这个问题很明显。

答案 1 :(得分:2)

垃圾收集器不一定会立即释放内存。

对于Objective-C垃圾收集器,您可以向Cocoa的垃圾收集器对象发送collectIfNeeded消息,以表明现在可能是进行一些收集的好时机,或collectExhaustively可以订购它立即开始收集任何和所有垃圾(但即使这是可中断的)。请参阅the docs

答案 2 :(得分:0)

我在iPhoneOS 3.2中有一个非常类似的问题,我真的不认为内存正在被回收 - 我最终会触发内存警告。我很少有机会忽视自己的错误,但我已经非常彻底了。

我使用NSKeyedUnarchiver的unarchiveObjectWithFile:来加载包含单个大型NSData和另一个小得多的对象的自定义对象。我的自定义对象中的dealloc方法被调用,NSData对象被释放,其retainCount == 1就在之前。物理内存不会减少任何数量,更不用说NSData大小的一小部分,并且可靠地生成重复内存警告:我已经测试,直到我实际收到2级警告。 =(

发布前:

(gdb)p(int)[(NSData *)pastItsWelcomeData retainCount]
$ 1 = 1

发布后:

(gdb)p(int)[(NSData *)pastItsWelcomeData retainCount]
Target不响应此消息选择器。