Re: Excessive NSImage caching (or leaking)
Re: Excessive NSImage caching (or leaking)
- Subject: Re: Excessive NSImage caching (or leaking)
- From: David Remahl <email@hidden>
- Date: Sat, 26 Apr 2003 01:00:19 +0200
After creating a very simple test case, I have found that the leak is
indeed dependent on wether the work is done on the main or the
secondary thread. There is no leak if the work is done on the main
thread, but if I detach a new thread, there is massive leakage.
According to Using the AppKit from Multiple Threads
<
http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/
ProgrammingTopics/Multithreading/Tasks/appkit.html#//apple_ref/doc/uid/
20000743/BAJJDIHH> creating views and windows on non-main threads is
allowed, but "There is some possibility that window objects may leak in
an application that deals with a lot of windows concurrently".
Does anyone know of a trick to make the leak stop? Some cleanup method
to call every now and then to let the graphics system release stuff?
Otherwise I will have to redesign and have the program do the work on
the main thread.
If someone is interested in having a look, here is the test case:
<
http://ittpoi.com/IconImageBug.tgz> (17 kB)
/ Rgds, David
On Wednesday, April 23, 2003, at 09:10 PM, David Remahl wrote:
Dear list,
I have a problem with some code dealing with NSWorkspace / NSImage
leaking loads and loads (well, relatively speaking, anyway) of memory.
The code that leaks uses -[NSWorkspace iconForFile:(NSString*)file] to
retrieve the icon of a file at a certain place.
The code runs in a separate thread, rigged with three autorelease
pools that empty at certain levels.
Since I'm only interested in the biggest of the icon sizes present, I
extract the correct NSBitmapImageRep from the image. In essence, this
is the code I'm using:
- (void)thread
{
while( !quit )
{
NSAutoreleasePool *autoPool = [[NSAutoreleasePool alloc] init];
somePath = [self getAPath]; /* some path indicates a different file
for each pass through the loop. */
NSBitmapImageRep *largestRep = [self
getIconRepForFileAtPath:somePath];
unsigned char *bmpData = [lagestRep bitmapData];
// do whatever to bmpData;
[autoPool release];
}
}
- (NSBitmapImageRep *)getIconRepForFileAtPath:(NSString *)path
{
NSImage *icon= [[NSWorkspace sharedWorkspace] iconForFile:path];
NSArray *reps = [icon representations];
NSBitmapImageRep *rep = nil;
if( [reps count] )
rep = [reps objectAtIndex:0];
return rep;
}
Profiling and memory debugging using OmniObjectMeter (and ObjectAlloc)
reveal a lot of interesting things about this code.
One curious thing about the code is that while [[NSWorkspace
sharedWorkspace] iconForFile:path] executes in almost no time, the
thing that takes a lot of time is the [icon representations] message.
According to the profiling, most of the execution time is then spent
to actually read the icon data - ie, it is loaded lazily when needed.
My first question: Is there a way to avoid the lazy loading of _all_
the representations, when the only one I'm really interested in is
representation at index 0? There is no public NSImage way to get at
just a single representation, and the private interfaces are way too
contrived for me to even think of using them.
My other question concerns the actual memory leak. It turns out, that
for each loop in -thread, quite a few objects leak, even though
everyone can see that I have properly balanced everything. The
following objects leak more or less once per representation per loop:
NSView
NSWindowGraphicsContext
CFArray (multiple variable)
CFArray (store-deque)
Anonymous bytes (probably cached data related to the above classes)
Note that no NSImages leak, nor do any NSBitmapImageReps.
Analyzing the retain count history of the objects above (in OOM), I
find that they are one release short. They all came into existence in
[NSImage representations] and were all retained / released /
autoreleased a number of times.
I'm not sure wether this is a real bug, or if its just some aggressive
caching policy gone terribly wrong. Or if it perhaps is a side-effect
of me using it from a secondary thread.
But the end result is a massive leakage. It is a difficult bug to work
around, since I don't have any references to the objects that leak. It
should be noted that the problem seems to be in how icns images are
handled. If I replace the NSWorkspace call with something like this:
NSImage *icon = [[[NSImage alloc]
initWithContentsOfFile:@"/path/to/someIcon.icns"] autorelease];
the net result is the same.
I request suggestions as to how to solve this...Please don't suggest I
use the IconFamily class, since it uses initWithData: on the icns data
to implement its -imageWithAllReps method.
The only solution I can see, is to reimplement all of
NSWorkspace/NSImage's code to go from an IconRef to an icon image.
That is what is being done in [icon representations] by means of
various PlotIconRefTo* function calls (more or less private).
/ Sincerely, David Remahl
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.