CoreGraphics memory issue

Member
Posts: 73
Joined: 2009.03
Post: #1
My GLSprite class loads images to create textures like the example program does. it creates a CoreGraphics bitmap context then draws the sprite data onto it, then releases the bitmap context.

HOWEVER, the memory used by the bitmap contexts are not freed right away. I am having problems because as much as 9 MB of memory is being retained much longer than it needs, unnecessarily pushing my app's memory usage over 20 MB and causing low memory warnings or just quitting.

There seems to be some kind of usage threshold before garbage collection actually frees the memory (it does eventually get freed, so I know it's not a leak). Is there a way to force the memory to be freed immediately after releasing the graphics context? I tried using a NSAutoreleasePool with [drain] around the code that creates and releases the bitmap context, but that didn't help at all.

Thanks.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #2
Gillissie Wrote:There seems to be some kind of usage threshold before garbage collection actually frees the memory

Garbage collection doesn't exist on iPhone OS. Something else is going on in your code.

Apple's code isn't free of leaks/memory inefficiencies, so if you can reasonably determine that it's their code causing the problem, put together a test project and submit it to bugreporter.apple.com. But be sure it's not your own code first. Smile
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #3
Are you by chance using [UIImage imageNamed:]? If that's the case, use [UIImage imageWithContentsOfFile:] instead.
Quote this message in a reply
Member
Posts: 73
Joined: 2009.03
Post: #4
AnotherJake Wrote:Are you by chance using [UIImage imageNamed:]? If that's the case, use [UIImage imageWithContentsOfFile:] instead.

Actually, yes. So I changed it as you suggested, but now my images aren't loading at all. I'm just getting black. Still investigating, but if you have any tips I'm all ears.

[edit] imageWithContentsOfFile is returning nil. imageNamed returns the object. hmm.
Quote this message in a reply
⌘-R in Chief
Posts: 1,260
Joined: 2002.05
Post: #5
I'd guess that you're not passing the correct path and are actually using a nil image reference. imageNamed: is just a convenience method which goes through the same path as imageWithContentsOfFile:.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #6
Try something like:
Code:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"]];
Quote this message in a reply
Member
Posts: 73
Joined: 2009.03
Post: #7
Yes, I discovered this shortly after my last response. It is now loading the images correctly.

This method is much better, but still not perfect. The really odd thing is that the extra memory isn't freed until after I've actually drawn the GL Sprite that uses the texture at least once. This forces me to use some strange code to pre-load my textures during startup. Basically...

Code:
GLSprite *temp = [GLSprite create, blah];
[temp draw];

One for each texture image. If I don't do this, the first time I need to draw the sprites for real, the memory usage racks up for a little while, then frees. The total memory usage is higher too.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #8
I've heard of that happening, but I don't think I've actually measured it myself. I usually use CGImageCreateWithPNGDataProvider to get my image ref for loading. I don't know if that makes a difference. Pseudocode (complete with lame error handling):

Code:
NSString *path = [[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"];
if (path == nil)
{
    NSLog(@"texture not found");
    return;
}
NSURL *url = [NSURL fileURLWithPath:path];
if (url == nil)
{
    NSLog(@"unable to create URL");
    return;
}
CGDataProviderRef source = CGDataProviderCreateWithURL((CFURLRef)url);
CGImageRef image = CGImageCreateWithPNGDataProvider(source, nil, NO, kCGRenderingIntentDefault);
if(image == nil)
{
    NSLog(@"load failed");
    return;
}

... load using CG as usual here

CGImageRelease(image);
CGDataProviderRelease(source);
CGContextRelease(context);
free(pixels);
Quote this message in a reply
Member
Posts: 73
Joined: 2009.03
Post: #9
AnotherJake,

Wow, this is pretty sweet. With this code the memory is released very quickly, AND I don't need to do that funky pre-loading code. Now my memory usage maxes out at about 9 MB instead of about 15 MB in a best-case scenario.

I think the main difference here must be the fact that this technique explicitly releases the sprite image with CGImageRelease, which the default technique does not. Although iPhones don't have garbage collection, something is delaying the cleanup of image data with the original technique. It would be nice if the official GLSprite example used this code instead.

Thank you very much!!!
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #10
Excellent, glad to hear that Smile
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #11
This didn't work?

Code:
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"]];
// Use image
[img release];
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Problems rendering CoreGraphics to a CGLayer... MisterMo 0 2,364 Feb 16, 2009 12:10 AM
Last Post: MisterMo