I'm trying to write a command line program to extract thumbnails from a large number of arbitrary images but am hitting what looks like a fundamental memory leak in NSImage.
Even with all the other code that does anything useful stripped out, just repeatedly opening and closing an image uses up more and more memory. After a few minutes (and 40,000 iterations or so) memory usage gets up to 100 GB or more according to Activity Monitor and then either "Your system has run out of application memory" is displayed or the process just dies. This is on an M1 Mac with 32GB RAM running Monterey 12.6.
My test code is as follows. As well as using CFRelease I also tried with an autorelease pool and calling [image release]. Is there anything else I should be doing to stop it running out of memory?
char* path = "~/Desktop/IMG_9999.JPG";
for (int i=0; i<1000000; i)
{
NSString* str = [NSString stringWithUTF8String:path];
NSImage *image = [[NSImage alloc]initWithContentsOfFile:str];
NSSize size = [image size];
CFRelease(image); // also tried [image release] and @autorease { ... }
CFRelease(str);
}
Here is a small self-contained test using autorelease. Compile with "/usr/bin/g -o test -stdlib=libc -framework AppKit test.mm" and run with "./test /path/to/image.jpg" (or .heic or .png etc.)
#import <AppKit/AppKit.h>
int main(int argc, const char** argv)
{
const char* path = argv[1];
for (int i=0; i<1000000; i)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* str = [NSString stringWithUTF8String:path];
NSImage* image = [[NSImage alloc]initWithContentsOfFile:str];
NSSize size = [image size];
if (i==0)
printf("size is %f x %f\n", size.width, size.height);
else if (i00==0)
printf("repeat %d\n", i);
[pool release];
}
}
CodePudding user response:
[NSString stringWithUTF8String:path]
returns an autoreleased object but [[NSImage alloc]initWithContentsOfFile:str]
doesn't. You need both an autorelease pool and [image release]
.
int main(int argc, const char** argv)
{
char* path = "/Volumes/Macintosh HD/Users/willeke/Desktop/Test image.png";
for (int i=0; i<10000; i)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* str = [NSString stringWithUTF8String:path];
NSImage* image = [[NSImage alloc] initWithContentsOfFile:str];
NSSize size = [image size];
if (i==0)
printf("size is %f x %f\n", size.width, size.height);
else if (i00==0)
printf("repeat %d\n", i);
[image release];
[pool release];
}
}