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: Jeff Schilling <email@hidden>
- Date: Wed, 2 Jun 2010 23:28:23 -0400
Are you running with NSZombieEnabled=YES? many an hour has been spent tracking down 'memory leaks' - I have some firsthand experience with this :-)
On Jun 2, 2010, at 9:51 AM, Ken Tozier 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