Using NSAutoreleasePool only via Objective-C runtime functions - objective-c

I'm learning how memory management works in Objective-C. From what I've learned, objects that are marked autorelease will be added to the enclosing NSAutoreleasePool and be released whenever the pool is released/drained.
To try out my new knowledge I created some tests; Creating 1 million objects used ~17mb, creating the same objects but immediately releasing them used ~1mb. However, I cannot get NSAutoreleasePool to work, using the code below still uses ~17mb and I do not understand why.
#include <objc/objc-runtime.h>
#include <stdio.h>
int main(void) {
Class NSAutoreleasePool = objc_getClass("NSAutoreleasePool");
Class Object = objc_getClass("NSObject");
SEL new = sel_registerName("new");
SEL drain = sel_registerName("drain");
SEL autorelease = sel_registerName("autorelease");
id pool = objc_msgSend(NSAutoreleasePool, new);
for (int i = 0; i < 1e6; i++) {
id obj = objc_msgSend(Object, new);
objc_msgSend(obj, autorelease);
}
objc_msgSend(pool, drain);
printf("OK\n");
getchar();
return 0;
}

When you release each object immediately after creating it, there's never more than one object in existence at a time. So the runtime can recycle the same memory for each new object. The runtime only has to ask the operating system for enough memory to hold that one object.
When you autorelease each object after creating it, each object lives until the autorelease pool is drained, so you end up with 1,000,000 objects existing simultaneously. The runtime has to ask the operating system for enough memory to hold all of the objects. And since the runtime doesn't know in advance how many objects you intend to create, it asks the operating system for chunks of memory incrementally. Maybe it asks for 1 MiB, and when that 1 MiB is exhausted, it asks for another 1 MiB, and so on.
Generally, when your program frees some memory (in this case because it destroys an object), the runtime doesn't return that memory to the operating system. It keeps the memory available so that it can reuse it the next time you create an object.
The operating system knows how much memory it has given to your process, but it doesn't know which parts of that memory are (within the process) consider “in use” and which parts are considered “free”.
Activity Monitor and top both ask the operating system for information about your process. So they only know what the operating system knows, which is how much memory has been given to your process. They can't know how much of that memory is in use and how much is free.
If you want a more accurate picture of how much memory is in use by live objects in your program, you need to use a more invasive tool. The Instruments program, which comes with Xcode, can show you how much memory is in use by “live allocations” using the Allocations instrument.
Here's what the Allocations instrument shows, when I run your program under it for about three seconds:
So you can see that the Allocations instrument detected a total of 1,001,983 “transient” allocations. A transient allocation is a chunk of memory that was allocated and then freed before Instruments stopped recording.
You can also see that the “persistent” bytes allocated (which is memory allocated and not freed by the time Instruments stopped recording) is 506 KiB, but the total bytes allocated (including bytes later freed) is 23.5 MiB.

Related

free function not working in c / objective-c [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do malloc() and free() work?
I have encountered a weird problem and I'm really not sure why it doesn't work.
I have the following code in Xcode:
void *ptr = malloc(1024 * 1024 * 100);
memset(ptr, 0, 1024 * 1024 * 100);
free (ptr); //trace this line
ptr = malloc (1024 * 1024 * 100);
memset(ptr, 0, 1024 * 1024 * 100);
free (ptr); //trace this line
I put a breakpoint on each of the free() line, and when I traced the program, free didn't really free up the 100mb. However, if I change the number from 100 to 500 (allocate 500mb twice), memset 500mb, free() works fine. Why?
free can never fail(it does not have a return value) unless you call it with a improper address, which gives you undefined behavior.
You do not have to bother whether free actually frees memory or not you just have to ensure that you call free on the correct address after you are done with dynamic memory usage, rest the compiler should take care for you.
This is one of those things that you should just believe on your compiler to handle correctly.
Also, free just marks the memory being deallocated free(as name says) for reuse. It does not zero out or initialize the memory being deallocated.
When you pass a block of memory to free, that memory does not necessarily get returned to the operating system right away. In fact, based on the wording in the C standard, some argue that the memory can't be returned to the OS until the program exits.
The wording in question is (C99, §7.20.3.2/2): "The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation." Their argument is that when/if a block of memory is allocated and then freed, it should be available for allocation again -- but if it's returned to the OS, some other process might take it, so it's no longer available for further allocation, as the standard requires. Personally, I don't find that argument completely convincing (I think "allocated by another process" is still allocation), but such is life.
Most libraries allocate large chunks of memory from the OS, and then sub-allocate pieces of those large chunks to the program. When memory is freed by the program, the put that block of memory on an "available" list for further allocation. Most also (at least at times) walk through the list of free blocks, merging free blocks that are adjacent addresses.
Many also follow some heuristics about what memory to keep after it's been freed. First, the keep an entire block as long as any of the memory in that block remains in use. If, however, all the memory in a block has been freed, they look at its size, and (often) at how much free memory they have available. If the amount available and/or size of the free block exceeds some threshold, they'll usually release it back to the OS.
Rather than having fixed thresholds, some try to tailor their behavior to the environment by (for example) basing their thresholds on percentages of available memory instead of fixed sizes. Without that, programs written (say) ten years ago when available memory was typically a lot smaller would often do quite a bit of "thrashing" -- repeatedly allocating and releasing the same (or similar) size blocks to/from the OS.
free() does not have to immediately unmap and return to the OS the pages backing up previously but no longer allocated buffers. It may keep them around so you can allocate memory quickly again. When the program finishes, the pages will be unmapped and returned to the OS.
As others already said, free() doesn't have to return memory to the OS. But I reject an idea that you should never care whether the memory is returned. There should be a good reason to care, but there are valid reasons.
If you do want to return memory to OS, use a platform-specific way which provides this guarantee:
mmap with MAP_ANONYMOUS on systems supporting it (there are many, but MAP_ANONYMOUS is not POSIX): mmap instead of malloc, munmap instead of free.
VirtualAlloc and VirtualFree on Windows.
[Shoul I add something here for other systems? Feel free to suggest.]
These ways of allocating memory work with big memory units (system page size or more).

Difference between free() and release [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What are the differences between free, dealloc, release, and autorelease?
I want to get rid of my allocated memory used in my app. I know I need to use release but what is the difference between free() and release? Are they the same?
free() is part of the C standard library, so it's a function. It immediately frees the allocated memory obtained using malloc(), so it must be passed a pointer that is allocated by malloc(), else it invokes undefined behavior.
- release is a method (as opposed to a function) of the NSObject class. It does not immediately free memory; it only decrements an object's reference count by one. It then also checks for it being 0 - if it is zero, it invokes - dealloc (which is usually overridden by a subclass to free memory allocated by the constructor method, - init or free() memory allocated by malloc()).
So they are not the same at all, do not even attempt to use them interchangeably!
The more interesting part is how free works: (and in this direction, malloc too can be understood better)
In many malloc/free implementations, free does normally not return the memory to the operating system (or at least only in rare cases). The reason is, that you will get gaps in your heap and thus it can happen, that you just finish of your 2 or 4 GB of virtual memory with gaps. This should be avoided of course, since as soon as the virtual memory is finished, you will be in really big trouble. The other reason of course is, that the OS can only handle memory chunks that are of a specific size and alignment. To be specific: Normally the OS can only handle blocks that the virtual memory manager can handle (most often multiples of 512 Bytes eg. 4KB).
So returning 40 Bytes to the OS will just not work. So what does free do?
Free will put the memory block in its own free block list. Normally it also tries to meld together adjacent blocks in the address space. The free block list is just a circular list of memory chunks which have of course some admin data in the beginning. This is also the reason, why managing very small memory elements with the standard malloc/free is not efficient. Every memory chunk needs additional data and with smaller sizes more fragmentation happens.
The free-list is also the first location, malloc looks for a new chunk of memory when needed. It is scanned before it calls for new memory from the OS. When a chunk is found that is bigger then the needed memory, it is just divided into two parts. One is returned to caller, the other is put back into the free list.
Release:Cocoa uses certain naming conventions. Anything that starts with alloc, new, or copy returns something with a retainCount of 1 and you are required to release.When release is called the reatinCount decrements by 1

What does ActivityMonitor Memory Usage mean?

I have an application which allocates a large number of objects (mostly of 3 classes) and occasionally releases these.
The ActivityMonitor Real Memory Usage only ever goes up, never down. (Indeed I have noticed this with other applications.)
Profiling shows my application has no leaks, and Garbage Collections shows objects being reclaimed.

How does XCode memory leak detection work?

How does the XCode Instrument Leak tool figure out if an object is a leak or just something not released yet?
I'm pretty new to Objective C, the leak tool detects a leak in the code I work with. But the code looks sound to me. So just wondering how much can I trust this tool?
A "leak" as an object that's still allocated, but your application no longer has a reference
pointing to that object. Since you no longer have a reference, there's no way you will be able to release the object, thus it's a leak.
As the leaks(1) man page says:
leaks identifies leaked memory -- memory that the application has allocated, but has been lost and cannot be freed. Specifically, leaks examines a specified process's memory for values that may be pointers to malloc-allocated buffers. Any buffer reachable from a pointer in writable memory, a register,
or on the stack is assumed to be memory in use. Any buffer reachable from a pointer in a reachable
malloc-allocated buffer is also assumed to be in use. The buffers which are not reachable are leaks;
the buffers could never be freed because no pointer exists in memory to the buffer, and thus free()
could never be called for these buffers
You might also want to look into the ObjectAlloc tool in Instruments.

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. 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):
32bit i386 no-GC: all memory is freed instantly.
32bit i386 GC: almost all memory is freed instantly. the rest some time later.
64bit x86_64 no-GC: minimal memory is freed. like 10%
64bit x86_64 GC: no memory at all is freed. the memory stays reserved for hours. (activity mon)
i'm using LLVM with 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. 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. When I switch the build to 64bit x86_64 the release does nothing. The 20mb stay allocated.
upper pic 32bit lower 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. There is no GC running. (With GC enabled the same problem appears.)
Update 2:
The Same behavior can be observed when I create a new cocoa app from scratch with only the upper code in applicationDidFinishLaunching:. In 64bit mode the memory is not released. i386 works as expected.
The same problem appears with NSString instead of NSData. It also appears when I boot the 64bit kernel. (Holding 64 at startup.)
OS is 10.6.0
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.
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.
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.