我一直在尝试不同版本的应用程序,似乎发生了一些奇怪的事情:
我的应用有 5mb 的闲置占用空间。上传文件时,文件大小的内存被保留。上传后保留的内存应该被释放。现在构建有所不同(gc = 垃圾收集器):
- 32 位 i386 无 GC:立即释放所有内存。
- 32 位 i386 GC:几乎所有内存都立即释放。稍后休息。
- 64 位 x86_64 无 GC:释放的内存最少。像 10%
- 64 位 x86_64 GC:根本没有释放任何内存。内存会保留数小时。 (事件周一)
我正在将 LLVM 与 CLANG 结合使用。我一直在运行今天的仪器,并检查泄漏/僵尸/等。一切似乎都很干净。 (该应用程序相当简单。)
是否有对此行为的解释?
更新:
这是一些奇怪的东西。我把问题归结为:
我将一个 20mb 的文件加载到 NSData 中并释放它。我在没有启用任何垃圾收集的情况下这样做。代码是:
NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigshit"];
[bla release];
当我为 i386 32 位构建时,分配并释放了 20mb。当我将构建切换到 64 位 x86_64 时,该版本什么也不做。 20mb 保持分配状态。
upper pic 32bit lower 64 http://kttns.org/zguxn
除了上一个是为 32 位构建的而下一个是为 64 位构建的,这两个应用程序之间没有区别。没有 GC 在运行。 (启用 GC 后会出现同样的问题。)
更新 2:
当我仅使用 applicationDidFinishLaunching: 中的上层代码从头开始创建一个新的 Cocoa 应用程序时,可以观察到相同的行为。在 64 位模式下,内存不会被释放。 i386 按预期工作。
同样的问题出现在 NSString 而不是 NSData 上。当我启动 64 位内核时它也会出现。 (启动时保持 64。)
操作系统是 10.6.0
最佳答案
首先,使用Instrument的Object Graph工具验证内存不再被认为是在使用中;在某处没有保留计数或强引用。
如果它不再被使用,那么内存就会一直存在只是因为你没有达到收集器关心的阈值。
但是,这个声明:
64bit x86_64 no-GC: minimal memory is freed. like 10%
让我警惕。具体来说,如果您的代码设计为在非 GC 中工作——使用保留/释放——那么 (a) 您有内存泄漏,并且如果使用 CFRetain 或某种全局缓存,这可能会影响 GC 或 (b ) 你没有使用正确的工具来确定你是否有内存泄漏。
那么,您如何确定您正在泄漏内存?
更新;您正在使用事件监视器来监视进程的 RSIZE/VSIZE。这实际上不会告诉您任何有用的信息,除了“我的过程是否随着时间的推移而增长”。
很有可能(我没看过源代码),这段代码:
NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigpoop"];
将导致 20MB 的文件被 mmap()
加载到进程中。根本不涉及 malloc() 样式分配。相反,操作系统将 20MB 的连续地址空间交给您的进程,并将文件的内容映射到其中。当您读取 NSData 的内容时,它会在您读取文件时出现页面错误。
当您释放 bla
时,映射将被销毁。但这并不意味着 VM 子系统会将应用程序的地址空间减少 20MB。
因此,您正在消耗大量地址空间,而不是实际内存。由于您的进程是 64 位的,地址空间几乎是无限的资源,使用地址的成本非常低,这就是操作系统以这种方式实现的原因。
即没有泄漏,您的应用程序运行正常,无论是否有 GC。
这是一个常见的误解,因此给这个问题加了星标。
关于objective-c - cocoa 64 位二进制文件泄漏内存? (释放 NSData 不会释放内存),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1383642/