我用我的应用程序的不同版本玩了一段时间,看起来很奇怪:
我的应用程序空闲占用空间为5mb。当保存上传文件大小的文件时。上传后,应释放保留的内存。现在构建中存在差异(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
答案 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"];
将导致20MB文件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不响应此消息选择器。