I tried something out in my code to see the effect on memory utilization. I wanted to find out if the line inside the loop was leaking. Running this loop took utilization up to 100MB and it didn't go back down again. Does this indicate a memory leak? If so why? (I'm using ARC)
for (i = 0; i < 10000000; i++)
{
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
(accounts is an array of AccountSummary objects which implements NSCopying like this: name city state phone are all NSStrings, isLocal is BOOL)
- (id)copyWithZone:(NSZone *)zone {
AccountSummary *newAccount = [[AccountSummary allocWithZone:zone] init];
newAccount.name = [self.name copyWithZone:zone];
newAccount.city = [self.city copyWithZone:zone];
newAccount.state = [self.state copyWithZone:zone];
newAccount.phone = [self.phone copyWithZone:zone];
newAccount.isLocal = self.isLocal;
return newAccount;
}
There's no leak here that I can see. There will, however, be quite a bit of peak memory usage.
The exact behaviour of things that should logically release memory varies. Quite often, instead of releasing the memory it's autorelased. (With MRR, there used to be a method called autorelease.) When you autorelease something, it isn't really released but is instead scheduled for release later, when your code is finished because it's returned to the main event loop.
If part of this is being autoreleased — and my guess is that the property assignment is autoreleasing, because autorelease is "safer" than hard releasing — that memory won't be deallocated until your next autoreleasepool flush. Code on the main thread has an autoreleasepool set up by the OS itself, so each time you return to the main event loop everything that's been autoreleased gets flushed out. Here, that probably means that all 10,000,000 copies are kept in memory until you return to the main event loop. Darn right that'll crash a real device. :)
(That's assuming you're on the main thread; if you're not, you may not even have an autorelasepool set up, which means you probably will get a leak. But I think you get warnings to console in this case, so you'd already have a hint about which way to go.)
You can reduce this peak memory usage by using #autoreleasepool:
for (i = 0; i < 10000000; i++) #autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
What will happen now is that the memory scheduled for release later in each iteration of the loop will actually be released each iteration of the loop. That should solve your immediate problem.
That said, i can't imagine why you're doing this except to check the behaviour. And if that's the case, this is unlikely your core problem.
Assuming your core problem is a leak, with ARC you're not really looking for leaks. You're looking for circular references. That means your answer likely lies elsewhere in your code. If you're sure it's self's accounts rather than dsl's accounts that are leaking, look for self being involved in a circular loop.
Also, keep in mind that calling copyWithZone: on a NSString will probably not copy the string. (There's no need to copy a read-only string, as the read-only string can't be changed. Both "copies" can be the same object.) So if you're leaking just strings, they could be associated with the original objects.
When creating lots of objects inside a loop, you should do that inside an auto release pool.
#autoreleasepool {
for (i = 0; i < 10000000; i++) {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
or, more likely in the real world...
for (i = 0; i < 10000000; i++) {
#autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
Related
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Objective C Memory Management
My code is showing a memory leak here:
NSMutableArray* newImageArray = [[NSMutableArray alloc] init];
NSMutableArray* newMediaArray = [[NSMutableArray alloc] init];
if (self.categoryIndex == 0) {
for (int i=1; i < [categoryArray count]; i++)
{
newImageArray = [NSMutableArray arrayWithArray:[newImageArray arrayByAddingObjectsFromArray:[self getImageArrayByCategoryIndex:i]]];
}
}
else {
newImageArray = [self getImageArrayByCategoryIndex:self.categoryIndex];
}
for (int i=0; i < [newImageArray count]; i++)
{
Media* media = [[Media alloc] init];
NSString* imageFile = [newImageArray objectAtIndex: i];
media.imageFile = [UIImage imageNamed:imageFile];
media.imageLabel = [[imageFile lastPathComponent] stringByDeletingPathExtension];
media.soundFile = [appFolderPath stringByAppendingString:[[[imageFile stringByDeletingPathExtension] stringByAppendingString: #".wav"] stringByReplacingOccurrencesOfString: IMAGES_FOLDER withString: SOUNDS_FOLDER]];
[newMediaArray addObject:media];
}
self.mediaArray = newMediaArray;
[self setNextMediaIndex];
I am not releasing media because it is being used by newMediaArray (which is used by mediaArray, which is used my my main object). Shouldn't everything get released when I release my main object?
It looks like you are leaking all over the place in a variety of ways
newImageArray gets allocated but never released, additionaly you are overwriting the version that you allocated in the first line of you code with another version. So even if you released it at the end of this code segment, the wrong version would get released. It looks like you don't even need to allocate this one.
newMediaArray gets allocated but never released, you assign it to a property mediaArray if you are using #synthesize to create the code for that property, depending on how you declared that property, the setter will retain the value i.e. newMediaArray creating a leak.
media gets allocated but never released, it get added to a NSMutableArray which means it will get retained by the array. If your app crashes when you release media in the for loop the problem is somewhere else
The Memory Management Programming Guide is pretty much a must read
When an NSMutableArray such as newMediaArray adds an object, it will retain that object. You don't need to (nor should you) retain the object on the array's behalf. This is fundamentally how memory management in Objective-C works: each object retains the things it references, and releases them when finished. newMediaArray is its own object, so it'll manage its own references.
You should release media near the end of the body of your for loop because you're done using that object. If you don't release it then, you'll lose your reference to it and you'll have no way to release it in the future.
You do
[newMediaArray addObject:media];
that means that newMediaArray has done a retain on media. You can then release media (you should). The retain done in the array method will keep it alive as long as the array references it. If you don't release it in your method, the retain count will remain 2, and even if the array releases it, it will still be 1 and not be dealloc-ed.
You could do:
Media *media = [[[Media alloc] init] autorelease];
Then the autorelease pool will release it in time, but not before this method ends.
You need to have the [media release] statement at the bottom of the loop. newMediaArray should also be released after it is assigned to the mediArray property.
As a new comer to Objective-c and its memory management technique, comparing two pieces below. (the original code was exacted from apple.com autorelease pools)
Questions:
1. Do the two pieces achieve the same result?
2. Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
3. Does the 2nd code below violate anything as far as best practices? Performance?
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
for (NSString *fileName in args) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error] autorelease];
/* Process the string, creating and autoreleasing more objects. */
[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSString *fileContents = [[NSString alloc] autorelease];
for (NSString *fileName in args)
{
// NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
//[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
Do the two pieces achieve the same result?
No. It's bound to blow up and/or leak when fileContents = [fileContents ... is called the second time. Implementations assume their instance's memory is zeroed because that is guaranteed by the runtime.
Even if one initializer is proven to work without issue or leaks, many of them will not.
Don't try to 'renew' your objects like this - just please do it the normal way =)
The sequence (in a non-gc environment) should be: 1) alloc 2) init...(using designated initializer) 3) dealloc.
Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No. In addition to what is outlined in #1, you should not assume the address returned from alloc will be the same returned from the initializer. The initializer may choose to destroy the result of alloc and create or return another allocation. In this case, you will introduce a reference count imbalance (effect: leak and/or zombie).
Does the 2nd code below violate anything as far as best practices?
NSString *fileContents = [[NSString alloc] autorelease]; is weird, very unconventional. The first time I read it, I missed the error because it is so conventional for init...] to be placed between alloc and autorelease.
Does the 2nd code below violate anything as far as Performance?
Performance may suffer by removing the autorelease pool from the inner loop. When large allocations and/or high iterations occur, it's best to create small localized pools, like the original.
To answer your questions:
1) Do the two pieces achieve the same result?
No, they don't, mainly because the second example is incorrect, on two accounts.
Firstly, NSString *fileContents = [[NSString alloc] autorelease] makes very little sense. This line essentially allocates room for a string, and without even initializing it (and increasing its initial retain count to 1), telling it to put itself onto an autorelease pool. This is a big no-no.
And second, fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error] makes just as little sense. You're initializing a new object using old memory that has not been released or reset in any way, which is just undefined behavior. As Sherm Pendley mentioned, you cannot allocate space once and simply reuse that memory without concern, and since you have no idea how NSString is defined internally, this can either simply leak memory or very simply blow up in your face. Another big no-no.
The rest of the code appears to be fine (it does depend on what you're doing in the 'process' step). As others have pointed out, +alloc and -init are almost always paired with each other, but not just because of convention, but because that this pairing is simply the right thing to do. You must +alloc memory before you can begin to use it, and you must -init to get that memory zeroed and in the right condition to run properly. Any other combination might not be fit for use.
2) Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No, they definitely do not. The second one will leak large amounts of memory, and very well might not even run. The first once is definitely okay, but might not run efficiently due to the use of inner autorelease pools.
3) Does the 2nd code below violate anything as far as best practices? Performance?
Yes, it does, and in fact, even the first one might not run efficiently. Creating an inner autorelease pool might not be in your best interest, and might even hinder your program. The venerable Mike Ash did some testing with autorelease pools, and the results are a little surprising. It turns out that differently-sized autorelease pools will work better on different computers (back then, on his old computer, 100 objects was the most efficent per pool; for me, 10000 objects works a little bit better, and even then only marginally better than 1000 objects)
NSMutableArray *m_res = [NSMutableArray arrayWithCapacity:ticks];
double t = lo_t;
while (t <= hi_t) {
[m_res addObject:[NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithDouble:t] decimalValue]
]];
t += delta_t;
}
return [[NSArray arrayWithArray:m_res] retain];
It is supposed to return a persistent NSArray containing some values. I plan to call release on it when it is no longer needed. Is it ok or there's some bug, because when I call function containing this code my program stops working (and it's a memory issue not endless loop).
The code is safe (ie won't crash) but the last line is incorrect.
The arrayWithArray is doing nothing useful and the retain is a leak.
It should be
return m_res;
If you hava a crash the cause is elsewhere.
I am learning Objective-C right now, however, there is memory management puzzle here make me so confused.
Let me see, within one method, I create a NSMutableString, and return it.
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString stringWithString:#""];
// do something
return string;
}
The question is who is responsible to release this memory, calling or called?
Second example:
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString alloc]init] autorelease];
// do something
return string;
}
When memory has been released, it will be released at after return string;
or it will be released at call method and there is no reference to it.
The third one:
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString alloc]init]];
// do something
return string;
}
This time the calling method need to release this memory, is that right?
If you follow the rule, you allocated memory then you are responsible to release it. 90% of time this will work. Of course there are some exception. But in general it should be good.
In your first example, you don't have to release it because you didn't allocate memory yourself, it's the stringWithString that is responsible (I believe it's doing an auto release)
In your second and third example, you are allocating memory with alloc, thus you have to release the memory once you are done with it.
In you second example, you are using autorelease, it means it the memory allocated will eventually be released. (similar to garbage collection in the Microsoft managed code world).
I came across a problem that seems to be called "drowning" in autorelease pools.
My code creates and destroys objects correctly. However, I use some class methods that autorelease several variables without my knowing about it. Considering they loop thousands and thousands of times every minute... I find myself drowning in thousands of unreleased objects lost somewhere in the program.
2 questions:
So either I do not use class methods AT ALL - is that a permanent solution, or, even so, will some instance methods (Foundation, UIKit) still autorelease objects without my knowing about it?
or I can alloc an autorelease pool prior to calling class methods and draining it after calling the class methods - would this be a permanent solution?
Just because you don't use a class method doesn't mean you're going to avoid autoreleased objects. For example: NSString * path = [anotherString stringByAppendingPathComponent:#"foo"] returns an autoreleased object, yet no class methods were involved. As far as I know, the only way to avoid autoreleased objects is to not use objects. If you really want to go that route, check out CoreFoundation. (I do not recommend going that route.)
If you do have some crazy factory method that creates tons of autorelease pools, then I'd probably do something like this:
+ (id) crazyFactoryMethodWithFoo:(id)foo {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
id returnValue = nil;
//generate gobs of autoreleased objects
returnValue = [something retain];
[pool release];
return [returnValue autorelease];
}
By explicitly retaining the return value before draining the pool, you ensure that the return value will live beyond the destruction of the autorelease pool. You then balance that retain by autoreleasing the return value and returning it.
Dave DeLong has already answered your questions, but usually the only times when you fill the pool too high is when you're in a very tight loop. In this case, just create another autorelease pool for each iteration of the loop.
for (NSUInteger i = 0; i < 1000000000; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do the work
[pool release];
}
Objects are automatically added to the most recently created autorelease pool for the current thread. By creating one for each iteration of the loop, you can get rid of the unwanted objects quickly rather than letting them pile up in the run loop's pool.