hpique
hpique

Reputation: 120354

Image IO memory keeps growing

I ran my app in Instruments with the VM Tracker a discovered a growing Image IO memory consumption.

Instruments with initWithContentsOfFile:

Effectively, the app did quite a bit of reading images from disk with initWithContentsOfFile:. I read once that this method was the spawn of Satan, so I replaced it with the following:

NSData *data = [NSData dataWithContentsOfFile:path];
UIImage *image = [UIImage imageWithData:data];

This reduced virtual memory greatly (about 60%), as shown below:

Instruments with imageWithData:

But, why does the Image IO virtual memory keep growing over time, when there aren't any leaks and my app is just using 15MB of live memory?

Is there something I can do to make sure that this Image IO memory is released?

Basically, the image reading from disk is done like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
    NSData *data = [NSData dataWithContentsOfFile:path];
    UIImage *image = [UIImage imageWithData:data];
    dispatch_async(dispatch_get_main_queue(), ^{
        imageView.image = image;
    });
});

I also tried the following without any significant changes:

Which makes me think the problem might be elsewhere.

Upvotes: 4

Views: 3143

Answers (2)

hpique
hpique

Reputation: 120354

Thanks to Heapshot Analysis I found that the images were retained by a NSCache.

The problem was that the NSCache didn't release any objects on memory warning. The solution: make it observe UIApplicationDidReceiveMemoryWarningNotification and remove all of its objects when this happens. This is how Instruments looks now:

Instruments with memory warning draining

Instruments is awesome. Thanks to @NikolaiRuhe and @nielsbot for making me dig deeper.

Also, I should add that memory consumption lowered when using NSData because dataWithContentsOfFiledoesn't account for retina files (d'uh). So imageWithContentsOfFile: might still be the spawn of Satan, but this one wasn't its fault.

Upvotes: 1

Nikolai Ruhe
Nikolai Ruhe

Reputation: 81878

You should at least wrap the background processing into an autorelease pool:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
    @autoreleasepool {
        NSData *data = [NSData dataWithContentsOfFile:path];
        UIImage *image = [UIImage imageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = image;
        });
    }
});

This way you make sure any autoreleased objects on the background thread go away as fast as possible.

Upvotes: 2

Related Questions