+2  A: 

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). See the docs.

Peter Hosey
+9  A: 

First, use Instrument's Object Graph instrument to verify that the memory is no longer considered to be in use; 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. like 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.

So, how are you determining that you are leaking memory?

Update; you are using Activity Monitor to monitor the RSIZE/VSIZE of the process. 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. There isn't a malloc() style allocation involved at all. Instead, the OS hands 20MB of contiguous address space to your process and maps the file's contents into it. As you read the contents of the NSData, it'll page fault in the file as you go.

When you release bla, the mapping is destroyed. But that doesn't mean that the VM subsystem is going to reduce your application's address space by 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.

I.e. there is no leak and your app is behaving correctly, GC or no.

This is a common misconception and, thus, star'd the question.

bbum
I'm using the memory leak instruments. And as a 32bit build everything works as expected.
Jaroslaw Szpilewski
please read my updates to the original question.
Jaroslaw Szpilewski
thanks for the answer. it seems you're right :)
Jaroslaw Szpilewski
A: 

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. 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. The dealloc method in my custom object gets called, the NSData object is released, its retainCount == 1 just before. 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. =(

Before release:

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

After release:

(gdb) p (int) [(NSData *) pastItsWelcomeData retainCount]
Target does not respond to this message selector.

ctpenrose
You have a different problem. GC doesn't exist on the iPhone, and `retainCount` is the wrong thing to use to debug object-lifetime problems anyway. (Use Instruments instead.) Since this is a description of a different problem and it doesn't answer the question, you should post it as a separate question.
Peter Hosey