Re: Monster memory leak and I can't figure out why
Re: Monster memory leak and I can't figure out why
- Subject: Re: Monster memory leak and I can't figure out why
- From: Ken Ferry <email@hidden>
- Date: Wed, 2 Jun 2010 21:09:05 -0700
// Option 2
NSImage *image
= [[NSImage alloc] initWithContentsOfFile: inPath];
/* do some stuff */
[image release];
It seems very likely that the error is in the "do some stuff".
I built an app from your code and sat it in a while loop, and it didn't
leak. It had the same behavior as you report for the second version of your
code.
-Ken
Cocoa Frameworks
On Wed, Jun 2, 2010 at 6:51 AM, Ken Tozier <email@hidden> wrote:
> Thanks all for the replies
>
> Re point #1: I was using Activity Monitor and both the "Real Memory" and
> "Virtual Memory" columns showed this 100+ MB leak every 5-10 seconds. After
> a minute or so of running, My app had gobbled up almost 2 gigs of memory.
>
> Re point #2: All the thumbnail conversion code is contained within this one
> method, so once it enters the loop, it doesn't exit till it's done.
>
> I tried using [[NSGarbageCollector defaultCollector] collectExhaustively],
> as Jonathan suggested, tried [image setCacheMode: NSImageCacheNever], but
> neither of those slowed the ferocious gobbling of memory.
>
> I finally resorted to using CGxxx functions and the problem disappeared. My
> App now hums along generating 12 to 15 thumbnails per second with a memory
> footprint in the 20 to 30 MB range. Here's what worked:
>
>
> - (NSString *) createJPEGThumbnail:(NSString *) inPath
> site:(NSString *) inSite
> {
> NSDictionary *siteRecord,
> *pubRecord;
>
> NSString *sourceName =
> [inPath lastPathComponent],
> *sourceRoot
> = [sourceName stringByDeletingPathExtension],
> *destName
> = [[sourceRoot stringByAddingPercentEscapesUsingEncoding:
> NSUTF8StringEncoding] stringByAppendingPathExtension: @"jpg"],
> *pubCode
> = [self pubCodeFromPath: inPath],
> *thumbPath;
>
> NSFileManager *manager =
> [NSFileManager defaultManager];
>
> siteRecord = [thumbDirectories
> objectForKey: inSite];
>
> pubRecord = [[siteRecord objectForKey:
> @"publications"] objectForKey: pubCode];
>
> if (pubRecord == nil)
> pubRecord = [[siteRecord objectForKey:
> @"publications"] objectForKey: @"~MISCELLANEOUS"];
>
> thumbPath = [[pubRecord objectForKey:
> @"thumb_path"] stringByAppendingPathComponent: destName];
>
> if (![manager fileExistsAtPath: thumbPath])
> {
> NSURL *sourceURL
> = [NSURL fileURLWithPath: inPath],
>
> *destURL = [NSURL fileURLWithPath: thumbPath];
>
> NSString *imageType
> = UtilPreferredUTIForFile(inPath);
> NSNumber *maxPixels
> = [NSNumber numberWithInt: maxDimension];
> NSDictionary *sourceOptions =
> [NSDictionary dictionaryWithObjectsAndKeys:
>
> imageType,
> kCGImageSourceTypeIdentifierHint,
>
> kCFBooleanFalse,
> kCGImageSourceShouldCache,
>
> kCFBooleanTrue,
> kCGImageSourceCreateThumbnailFromImageAlways,
>
> maxPixels,
> kCGImageSourceThumbnailMaxPixelSize,
>
> nil];
>
>
> CGImageSourceRef imageSourceCG =
> CGImageSourceCreateWithURL((CFURLRef) sourceURL, (CFDictionaryRef)
> sourceOptions );
> if (imageSourceCG != NULL)
> {
> NSDictionary *imageProps
> = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(imageSourceCG, 0,
> NULL);
> int w
> = [[imageProps objectForKey: @"PixelWidth"]
> intValue],
> h
> = [[imageProps objectForKey: @"PixelHeight"]
> intValue];
>
> NSRect thumbRect
> = [self thumbRectWithSize: NSMakeSize(w, h)];
>
> NSDictionary *destOptions
> = [NSDictionary dictionaryWithObjectsAndKeys:
>
> [NSNumber numberWithInt:
> thumbRect.size.width], kCGImagePropertyPixelWidth,
>
> [NSNumber numberWithInt:
> thumbRect.size.height], kCGImagePropertyPixelHeight,
>
> nil];
>
> CGImageRef imageRefCG
> = CGImageSourceCreateThumbnailAtIndex(imageSourceCG, 0,
> (CFDictionaryRef) sourceOptions);
> if (imageRefCG != NULL)
> {
> CGImageDestinationRef destRef =
> CGImageDestinationCreateWithURL((CFURLRef) destURL, kUTTypeJPEG, 1, NULL);
> if (destRef != NULL)
> {
> CGImageDestinationAddImage(destRef,
> imageRefCG, (CFDictionaryRef) destOptions);
> CGImageDestinationFinalize(destRef);
> CFRelease(destRef);
> }
> CFRelease(imageRefCG);
> }
>
> CFRelease(imageSourceCG);
>
> }
> }
>
> // make sure it worked
> if ([manager fileExistsAtPath: thumbPath])
> return thumbPath;
> else
> return nil;
> }
>
>
>
> On Jun 1, 2010, at 6:47 PM, Tony Romano wrote:
>
> Hi Ken,
>>
>> This code by itself should not be causing a leak. Couple of questions:
>>
>> 1. How do you know you have a memory leak? Sound like a silly question
>> but you didn't tell us anything about what you are using to detect leaks.
>> 2. Who is calling this code, how many times? If you are passing the
>> image to something else in the sections you have commented out and you are
>> calling this many times, your memory usage may grow senza any cache'ing that
>> maybe happening.
>>
>> I would suggest putting a NSLog statement at the beginning and look at
>> your output window to make sure the code is not called more than you think.
>>
>> -Tony Romano
>>
>> On May 31, 2010, at 6:41 PM, Ken Tozier wrote:
>>
>> Hi
>>>
>>> I'm trying to write a thumbnailer class that takes a path to a photo and
>>> creates a thumbnail at a user specified size. The code creates the
>>> thumbnails OK, but there's this monster memory leak, to the tune of about
>>> 100 MB every 3-4 seconds, that seems to be related to NSImage.
>>>
>>> What's happening is that if I comment out the line that initializes a new
>>> image, the memory leak disappears. I've tried forcibly releasing the images,
>>> tried autoreleasing them, tried creating a fixed size buffer into which all
>>> the images are read, nothing seems to work.
>>>
>>> I'm using garbage collection, so that along with the deliberate releasing
>>> of the images, makes me wonder why the collector isn't getting the hint,
>>> that it's ok to release the memory for these photos. Could someone point out
>>> what I'm doing in the following code that prevents the images from getting
>>> released?
>>>
>>> Thanks for any help
>>>
>>> - (NSString *) createJPEGThumbnail:(NSString *) inPath
>>> site:(NSString *) inSite
>>> {
>>> NSDictionary *siteRecord,
>>> *pubRecord;
>>>
>>> NSString *sourceName =
>>> [inPath lastPathComponent],
>>> *sourceRoot
>>> = [sourceName stringByDeletingPathExtension],
>>> *destName
>>> = [[sourceRoot stringByAddingPercentEscapesUsingEncoding:
>>> NSUTF8StringEncoding] stringByAppendingPathExtension: @"jpg"],
>>> *pubCode
>>> = [self pubCodeFromPath: inPath],
>>> *thumbPath;
>>>
>>> NSFileManager *manager =
>>> [NSFileManager defaultManager];
>>>
>>> siteRecord = [thumbDirectories
>>> objectForKey: inSite];
>>> pubRecord = [[siteRecord
>>> objectForKey: @"publications"] objectForKey: pubCode];
>>>
>>> if (pubRecord == nil)
>>> pubRecord = [[siteRecord
>>> objectForKey: @"publications"] objectForKey: @"~MISCELLANEOUS"];
>>>
>>> thumbPath = [[pubRecord
>>> objectForKey: @"thumb_path"] stringByAppendingPathComponent: destName];
>>>
>>> if (![manager fileExistsAtPath: thumbPath])
>>> {
>>> // I've tried both of these, didn't make the slightest
>>> difference.
>>> // Both leaked memory at a furious pace
>>>
>>> // Option 1:
>>> NSImage *image
>>> = [[[NSImage alloc] initWithContentsOfFile: inPath]
>>> autorelease];
>>>
>>> /* do some stuff */
>>>
>>>
>>> // Option 2
>>> NSImage *image
>>> = [[NSImage alloc] initWithContentsOfFile: inPath];
>>>
>>> /* do some stuff */
>>>
>>> [image release];
>>> }
>>>
>>> // make sure it worked
>>> if ([manager fileExistsAtPath: thumbPath])
>>> return thumbPath;
>>> else
>>> return nil;
>>> }
>>> _______________________________________________
>>>
>>> Cocoa-dev mailing list (email@hidden)
>>>
>>> Please do not post admin requests or moderator comments to the list.
>>> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>>>
>>> Help/Unsubscribe/Update your Subscription:
>>>
>>> This email sent to email@hidden
>>>
>>>
>> -Tony
>>
>>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
>
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden