简体   繁体   English

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

[英]cocoa 64bit binaries leak memory? (releasing NSData does not free memory)

i've been playing some time with different builds of my application and there seem strange things to happen: 我一直在玩我的应用程序的不同版本,似乎有些奇怪的事情要发生:

my app has a 5mb idle footprint. 我的应用程序有5mb空闲的足迹。 when uploading a file memory in size of the file is reserved. 当保存上传文件大小的文件时。 after the upload the reserved memory should be freed. 上传后,应释放保留的内存。 now there are differences in the builds (gc = garbage collector): 现在构建中存在差异(gc =垃圾收集器):

  • 32bit i386 no-GC: all memory is freed instantly. 32位i386 no-GC:立即释放所有内存。
  • 32bit i386 GC: almost all memory is freed instantly. 32位i386 GC:几乎所有内存都立即被释放。 the rest some time later. 其余的一段时间后。
  • 64bit x86_64 no-GC: minimal memory is freed. 64位x86_64 no-GC:释放最小内存。 like 10% 像10%
  • 64bit x86_64 GC: no memory at all is freed. 64位x86_64 GC:根本没有内存被释放。 the memory stays reserved for hours. 记忆保持数小时。 (activity mon) (活动星期一)

i'm using LLVM with CLANG. 我正在使用LLVM和CLANG。 i have been running today instruments all the time and was checking for leaks/zombies/etc. 我一直在运行今天的仪器,并检查泄漏/僵尸/等。 and everything seems to be clean. 一切似乎都很干净。 (the app is rather simple.) (该应用程序相当简单。)

is there an explanation for this behavior? 这种行为有解释吗?


Update: 更新:

That's some weird stuff. 那是一些奇怪的东西。 I've boiled the problem to this: 我把问题归结为:

I load a 20mb file into a NSData and release it. 我将一个20mb的文件加载到NSData中并释放它。 I'm doing this without any garbage collection enabled. 我这样做没有启用任何垃圾收集。 The code is: 代码是:

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

When I build for i386 32bit the 20mb are allocated and released. 当我为i386 32bit构建时,20mb被分配并释放。 When I switch the build to 64bit x86_64 the release does nothing. 当我将构建切换到64位x86_64时,版本什么都不做。 The 20mb stay allocated. 20mb住宿分配。

upper pic 32bit lower 64 http://kttns.org/zguxn 上部图片32位以下64 http://kttns.org/zguxn

There is no difference between the two apps except that the upper one is built for 32bit and the lower one 64bit. 两个应用程序之间没有区别,除了上面的一个是32位而下面的64位。 There is no GC running. 没有GC运行。 (With GC enabled the same problem appears.) (启用GC后,会出现同样的问题。)


Update 2: 更新2:

The Same behavior can be observed when I create a new cocoa app from scratch with only the upper code in applicationDidFinishLaunching:. 当我从头开始创建一个新的cocoa应用程序时,可以观察到相同的行为,只有applicationDidFinishLaunching中的高位代码: In 64bit mode the memory is not released. 在64位模式下,内存不会被释放。 i386 works as expected. i386按预期工作。

The same problem appears with NSString instead of NSData. NSString而不是NSData出现同样的问题。 It also appears when I boot the 64bit kernel. 当我启动64位内核时,它也会出现。 (Holding 64 at startup.) (启动时保持64位。)

OS is 10.6.0 操作系统是10.6.0

First, use Instrument's Object Graph instrument to verify that the memory is no longer considered to be in use; 首先,使用Instrument的Object Graph仪器验证内存不再被认为正在使用中; does not have a retain count or a strong reference somewhere. 在某处没有保留计数或强引用。

If it is no longer in use, then the memory is sticking around simply because you haven't hit the threshold at which the collector cares. 如果它不再使用,那么内存就会因为你没有达到收集器关心的阈值而坚持下去。

However, this statement: 但是,这句话:

64bit x86_64 no-GC: minimal memory is freed. 64位x86_64 no-GC:释放最小内存。 like 10% 像10%

Makes me wary. 让我警惕。 Specifically, if your code is designed to work in non-GC -- with retain/release -- then either (a) you have a memory leak and, if using CFRetain or some kind of global cache, that might impact GC or (b) you aren't using the right tools to figure out whether or not you have a memory leak. 具体来说,如果您的代码设计为在非GC中工作 - 使用retain / release - 那么(a)您有内存泄漏,如果使用CFRetain或某种全局缓存,可能会影响GC或(b)你没有使用正确的工具来判断你是否有内存泄漏。

So, how are you determining that you are leaking memory? 那么,你如何确定你在泄漏记忆?

Update ; 更新 ; you are using Activity Monitor to monitor the RSIZE/VSIZE of the process. 您正在使用活动监视器来监视进程的RSIZE / VSIZE。 This won't actually tell you anything useful beyond "is my process growing over time". 除了“我的流程随着时间的推移而增长”之外,这实际上并没有告诉你什么有用。

More likely than not (I haven't looked at the source), this code: 更可能(我没有看过源代码),这段代码:

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

Will cause the 20MB file to be mmap() 'd in to the process. 将导致20MB文件为mmap()进入该进程。 There isn't a malloc() style allocation involved at all. 根本没有涉及malloc()样式分配。 Instead, the OS hands 20MB of contiguous address space to your process and maps the file's contents into it. 相反,操作系统将20MB的连续地址空间交给您的进程,并将文件的内容映射到其中。 As you read the contents of the NSData, it'll page fault in the file as you go. 当您阅读NSData的内容时,它会在文件中出现页面错误。

When you release bla , the mapping is destroyed. 释放bla ,映射将被销毁。 But that doesn't mean that the VM subsystem is going to reduce your application's address space by 20MB. 但这并不意味着VM子系统会将应用程序的地址空间减少20MB。

So, you are burning up a bunch of address space, but not actual memory. 所以,你正在烧掉一堆地址空间,而不是实际的内存。 Since your process is 64 bits, address space is pretty much an infinite resource and there is very little cost to using addresses, thus the reason why the OS is implemented this way. 由于您的进程是64位,因此地址空间几乎是无限资源,使用地址的成本非常低,因此操作系统以这种方式实现的原因。

Ie there is no leak and your app is behaving correctly, GC or no. 即没有泄漏,你的应用程序表现正常,GC或没有。

This is a common misconception and, thus, star'd the question. 这是一个常见的误解,因此,这个问题很明显。

A garbage collector doesn't necessarily release memory immediately. 垃圾收集器不一定立即释放内存。

In the case of the Objective-C garbage collector, you can send Cocoa's garbage collector object a collectIfNeeded message to suggest that now may be a good time to do some collection, or collectExhaustively to order it to immediately start collecting any and all garbage (but even this is interruptible). 在Objective-C垃圾收集器的情况下,您可以向Cocoa的垃圾收集器对象发送一个collectIfNeeded消息,以表明现在可能是进行一些收集的好时机,或者collectExhaustively按顺序收集以命令它立即开始收集任何和所有垃圾(但是即使这是可以中断的)。 See the docs . 查看文档

I have a very similar problem in iPhoneOS 3.2 and I really don't think that the memory is being reclaimed -- I eventually trigger memory warnings. 我在iPhoneOS 3.2中有一个非常类似的问题,我真的不认为内存正在被回收 - 我最终会触发内存警告。 There is a small chance that I have overlooked a mistake of my own but I have been very thorough. 我忽略了自己的错误的可能性很小,但我已经非常彻底了。

I use NSKeyedUnarchiver's unarchiveObjectWithFile: to load a custom object that contains a single large NSData and another much smaller object. 我使用NSKeyedUnarchiver的unarchiveObjectWithFile:来加载包含单个大型NSData和另一个小得多的对象的自定义对象。 The dealloc method in my custom object gets called, the NSData object is released, its retainCount == 1 just before. 我的自定义对象中的dealloc方法被调用,NSData对象被释放,其retainCount == 1就在之前。 Physical memory does not decrement by any amount, let alone a fraction of the NSData size, and with repetition memory warnings are reliably generated: I have test until I actually received level 2 warnings. 物理内存不会减少任何数量,更不用说NSData大小的一小部分,并且可靠地生成重复内存警告:我已经测试,直到我实际收到2级警告。 =( =(

Before release: 发布前:

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

After release: 发布后:

(gdb) p (int) [(NSData *) pastItsWelcomeData retainCount] (gdb)p(int)[(NSData *)pastItsWelcomeData retainCount]
Target does not respond to this message selector. 目标不响应此消息选择器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM