NSImage TIFFRepresentation memory leak

  • Is there a known memory leaking problem with using NSImage -
    TIFFRepresentation in a multithreaded Cocoa environment?

    In my app, I do the TIFFRepresentation call on the main thread, using
    -performSelectorOnMainThread:::. It works and gives me a valid TIFF
    representation, but leaks like a '63 VW Bug. The representation ought
    to be returned autoreleased, and I have it in its own autorelease pool:
        NSImage *anImage = ...;
        . . .
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        [anImage lockFocus];
        NSData *nsData = [anImage TIFFRepresentation]; // <-- Leak City
        [anImage unlockFocus];
        [nsData writeToFile::];  // <-- No leak here
        [pool release];
        . . .
    The memory, ay, she adds up muy quickly, according to about 4X the
    size of the images run through it. If I comment out the
    TIFFRepresentation line, no leak occurs. I can't seem reproduce this
    in a simple standalone app, so there must be some sort of rather
    complex interaction going on somewhere. I really don't know how to
    track it down. Any ideas?

    Roland
  • On 29 nov 2006, at 21.25, Roland Torres wrote:

    > NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    > [anImage lockFocus];
    > NSData *nsData = [anImage TIFFRepresentation]; // <-- Leak City
    > [anImage unlockFocus];
    > [nsData writeToFile::];  // <-- No leak here
    > [pool release];

    Why do you bracket the call to TIFFRepresentation inside calls to
    lockFocus/unlockFocus?

    > Any ideas?

    Check the memory management history for your tiff-data using
    ObjectAlloc. That should tell you what's going on.

    j o a r
  • On Nov 29, 2006, at 12:39 PM, j o a r wrote:
    >
    > On 29 nov 2006, at 21.25, Roland Torres wrote:
    >
    >> NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    >> [anImage lockFocus];
    >> NSData *nsData = [anImage TIFFRepresentation]; // <-- Leak City
    >> [anImage unlockFocus];
    >> [nsData writeToFile::];  // <-- No leak here
    >> [pool release];
    >
    > Why do you bracket the call to TIFFRepresentation inside calls to
    > lockFocus/unlockFocus?

    There, just as a test. I've removed them; no difference.

    >
    >> Any ideas?
    >
    > Check the memory management history for your tiff-data using
    > ObjectAlloc. That should tell you what's going on.

    Here's the backtrace from ObjectAlloc stemming from the line in
    question. I'm not sure what it's saying, other than
    TIFFRepresentation keeps allocating more and more memory...?

    GLEngine-2560

      start
      _start
        main()
        NSApplicationMain
          -[NSApplication run]
          -[NSApplication sendEvent:]
            -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]
            _DPSNextEvent
              BlockUntilNextEventMatchingListInMode
              ReceiveNextEventCommon
                RunCurrentEventLoopInMode
                CFRunLoopRunSpecific
                  __CFRunLoopRun
                  __CFRunLoopDoSources()
                    __CFRunLoopPerformPerform
                    __NSFireMainThreadPerform
                      -[MyController writeImageToFile:]    <-- The
    method in my program is here
                      -[NSImage TIFFRepresentation]
                        -[NSImage
    TIFFRepresentationUsingCompression:factor:]
                        -[NSImage _cacheRepresentation]
                          -[NSImage _cacheRepresentation:stayFocused:]
                          -[NSImage drawRepresentation]
                            -[NSImage drawRepresentation:inRect:]
                            -[NSCIImageRep drawAtPoint:]
                              -[NSCIImageRep drawInRect:]
                              -[CIContext drawImage:inRect:fromRect:]
                                -[CICGContextImpl render:]
                                CGContextDrawImage
                                  CGContextDelegateDrawImage
                                  0x9475ccd0
                                    0x9475e538
                                    CGSImageDataLockWithReference
                                      img_data_lock
                                      CGAccessSessionGetBytePointer
                                        provider_get_byte_pointer
                                        provider_ensure_data
                                          fe_image_get_bitmap
                                          fe_image_render_
                                            fe_tree_render_image
                                            fe_tree_render_image_
                                              fe_tree_prepare_image
                                              fe_tree_expand_2
                                                fe_tree_expand_2
                                                fe_tree_expand_2
                                                  fe_tree_expand_2
                                                  fe_tree_expand_2
                                                    fe_tree_expand_2
                                                    fe_tree_expand_2
                                                      fe_tree_expand_2

    fe_context_promote_format

    fe_gl_buffer_format_p
                                                        fe_accel_context
                                                          fe_accel_context_
                                                          create_context

    fe_cgl_create_account

    allocate_context

    CGLCreateContext

    cglInitializeContext
                                                                0x15380b8
                                                                0x1538e14
                                                                  0x153a980
                                                                  0x153aa44
    Roland
  • I think he was just helping you streamline your code in general. You
    don't need to focus on an NSImage or NSBitmapImageRep when asking for
    the TIFF representation.

    --
    mikey

    On 29 Nov, 2006, at 20:43, Roland Torres wrote:

    >> Why do you bracket the call to TIFFRepresentation inside calls to
    >> lockFocus/unlockFocus?
    >
    > There, just as a test. I've removed them; no difference.
  • On 30 nov 2006, at 02.43, Roland Torres wrote:

    > Here's the backtrace from ObjectAlloc stemming from the line in
    > question. I'm not sure what it's saying, other than
    > TIFFRepresentation keeps allocating more and more memory...?

    What I wanted to suggest was that you run ObjectAlloc in the mode
    where it records memory management events (alloc, retain, release,
    autorelease), and then see what events that have been generated for
    these object instances.

    The backtraces will be important when you want to verify when / why
    the memory management events occurred, but before you get to that,
    you would want to see what types of events happened in the first place.

    j o a r
  • FYI, this is a textbook case of an OS leak. you (or at least i) can
    easily tell from the backtrace. you are only responsible for
    releasing a reference that's returned by a method or function. just
    file a bug and move on. if it's a significant leak, you have to come
    up with a workaround.

    in this case, a reference returned by CGLCreateContext (or
    allocate_context. it doesn't matter) is the leak, and it was never
    returned to you since -[NSImage TIFFRepresentaion] only returns an
    autoreleased NSData object (not a CGLCreateContext). obviously, you
    can't directly release a reference returned by CGLCreateContext since
    you don't even have access to it.

    in short, if you didn't create (allocate, copy, or retain) it, it's
    not your leak.

    katsura

    > Here's the backtrace from ObjectAlloc stemming from the line in
    > question. I'm not sure what it's saying, other than
    > TIFFRepresentation keeps allocating more and more memory...?
    >
    > GLEngine-2560
    >
    > start
    > _start
    > main()
    > NSApplicationMain
    > -[NSApplication run]
    > -[NSApplication sendEvent:]
    > -[NSApplication
    > nextEventMatchingMask:untilDate:inMode:dequeue:]
    > _DPSNextEvent
    > BlockUntilNextEventMatchingListInMode
    > ReceiveNextEventCommon
    > RunCurrentEventLoopInMode
    > CFRunLoopRunSpecific
    > __CFRunLoopRun
    > __CFRunLoopDoSources()
    > __CFRunLoopPerformPerform
    > __NSFireMainThreadPerform
    > -[MyController writeImageToFile:]    <-- The
    > method in my program is here
    > -[NSImage TIFFRepresentation]
    > -[NSImage
    > TIFFRepresentationUsingCompression:factor:]
    > -[NSImage _cacheRepresentation]
    > -[NSImage _cacheRepresentation:stayFocused:]
    > -[NSImage drawRepresentation]
    > -[NSImage drawRepresentation:inRect:]
    > -[NSCIImageRep drawAtPoint:]
    > -[NSCIImageRep drawInRect:]
    > -[CIContext drawImage:inRect:fromRect:]
    > -[CICGContextImpl render:]
    > CGContextDrawImage
    > CGContextDelegateDrawImage
    > 0x9475ccd0
    > 0x9475e538
    > CGSImageDataLockWithReference
    > img_data_lock
    > CGAccessSessionGetBytePointer
    > provider_get_byte_pointer
    > provider_ensure_data
    > fe_image_get_bitmap
    > fe_image_render_
    > fe_tree_render_image
    > fe_tree_render_image_
    > fe_tree_prepare_image
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    > fe_tree_expand_2
    >
    > fe_context_promote_format
    >
    > fe_gl_buffer_format_p
    > fe_accel_context
    > fe_accel_context_
    > create_context
    >
    > fe_cgl_create_account
    >
    > allocate_context
    >
    > CGLCreateContext
    >
    > cglInitializeContext
    > 0x15380b8
    > 0x1538e14
    > 0x153a980
    >
    > 0x153aa44
    > Roland
  • On 1 dec 2006, at 09.33, Hidetomo Katsura wrote:

    > FYI, this is a textbook case of an OS leak. you (or at least i) can
    > easily tell from the backtrace. you are only responsible for
    > releasing a reference that's returned by a method or function. just
    > file a bug and move on. if it's a significant leak, you have to
    > come up with a workaround.
    >
    > in this case, a reference returned by CGLCreateContext (or
    > allocate_context. it doesn't matter) is the leak, and it was never
    > returned to you since -[NSImage TIFFRepresentaion] only returns an
    > autoreleased NSData object (not a CGLCreateContext). obviously, you
    > can't directly release a reference returned by CGLCreateContext
    > since you don't even have access to it.
    >
    > in short, if you didn't create (allocate, copy, or retain) it, it's
    > not your leak.

    Based on the data presented in this thread so far I would not agree
    that your claim is necessarily true.

    ObjectAlloc shows backtraces to things that are allocated, but that's
    not the same thing as leaked.
    If the piece of data that is allocated in this backtrace is still
    live, that is most likely because its "owner" has not released /
    freed it yet. Note that the "leak event" is typically not the same
    thing as the allocation event!

    Presented with something that could be either [1] a memory leak in a
    framework, or [2] a memory leak in your code, you should always
    suspect your code - Unless the evidence to the contrary is overwhelming.

    In this case we don't even know what's being leaked - is it opaque
    malloced data? It should be of primary interest to see if the NSData
    instances returned from "-TIFFRepresentation" are leaked or not.
    This, and many other similar points of data, is still unknown.

    The best thing would of course be if this proposed leak could be
    isolated in a small sample program, and posed for others to have a
    look at.

    I'm not saying that you're necessarily wrong, only that I don't think
    we have enough information to assert that you're right.

    j o a r
  • I just caught sight of this thread, and went back to read Roland's
    original description. I have seen what appear to be NSImage-related
    leaks in a multi-threaded environment, too.

    Roland, I see that you point out the "TIFFRepresentation" call is
    being made on the main thread, but what of all the other use of the
    NSImage? All on the main thread, too?  I suspect you're using the
    same NSImage in more than one thread, and this is messing up the
    NSImage caching mechanism. In my experience NSImage leaks when given
    the opportunity to try to cache on more than one thread.

    It's been a long time but in my case I came to the conclusion that I
    would work around the problem by turning off caching on the images in
    question.  Perhaps it was a brute-force way of fixing the problem,
    but it did the trick and I didn't notice a major performance hit.
    This is a simple category method I added to NSImage:

    - (void) makeThreadSafe
    {
    [self setCachedSeparately:YES];
    [self setCacheMode:NSImageCacheNever];
    }

    I basically call [theImage makeThreadSafe] as quickly as possible
    after creating instances of any NSImage in the app.

    Hope this helps, at least to know that you're not alone :) Perhaps as
    a test you could try the above technique just to see if we're talking
    about the same phenomenon.

    Daniel

    On Dec 1, 2006, at 8:06 AM, j o a r wrote:

    >
    > On 1 dec 2006, at 09.33, Hidetomo Katsura wrote:
    >
    >> FYI, this is a textbook case of an OS leak. you (or at least i)
    >> can easily tell from the backtrace. you are only responsible for
    >> releasing a reference that's returned by a method or function.
    >> just file a bug and move on. if it's a significant leak, you have
    >> to come up with a workaround.
    >>
    >> in this case, a reference returned by CGLCreateContext (or
    >> allocate_context. it doesn't matter) is the leak, and it was never
    >> returned to you since -[NSImage TIFFRepresentaion] only returns an
    >> autoreleased NSData object (not a CGLCreateContext). obviously,
    >> you can't directly release a reference returned by
    >> CGLCreateContext since you don't even have access to it.
    >>
    >> in short, if you didn't create (allocate, copy, or retain) it,
    >> it's not your leak.
    >
    >
    > Based on the data presented in this thread so far I would not agree
    > that your claim is necessarily true.
    >
    > ObjectAlloc shows backtraces to things that are allocated, but
    > that's not the same thing as leaked.
    > If the piece of data that is allocated in this backtrace is still
    > live, that is most likely because its "owner" has not released /
    > freed it yet. Note that the "leak event" is typically not the same
    > thing as the allocation event!
    >
    > Presented with something that could be either [1] a memory leak in
    > a framework, or [2] a memory leak in your code, you should always
    > suspect your code - Unless the evidence to the contrary is
    > overwhelming.
    >
    > In this case we don't even know what's being leaked - is it opaque
    > malloced data? It should be of primary interest to see if the
    > NSData instances returned from "-TIFFRepresentation" are leaked or
    > not. This, and many other similar points of data, is still unknown.
    >
    > The best thing would of course be if this proposed leak could be
    > isolated in a small sample program, and posed for others to have a
    > look at.
    >
    > I'm not saying that you're necessarily wrong, only that I don't
    > think we have enough information to assert that you're right.
    >
    > j o a r
  • On Dec 1, 2006, at 6:30 AM, Daniel Jalkut wrote:

    > I just caught sight of this thread, and went back to read Roland's
    > original description. I have seen what appear to be NSImage-related
    > leaks in a multi-threaded environment, too.
    >
    > Roland, I see that you point out the "TIFFRepresentation" call is
    > being made on the main thread, but what of all the other use of the
    > NSImage? All on the main thread, too?  I suspect you're using the
    > same NSImage in more than one thread, and this is messing up the
    > NSImage caching mechanism. In my experience NSImage leaks when
    > given the opportunity to try to cache on more than one thread.
    >
    > It's been a long time but in my case I came to the conclusion that
    > I would work around the problem by turning off caching on the
    > images in question.  Perhaps it was a brute-force way of fixing the
    > problem, but it did the trick and I didn't notice a major
    > performance hit. This is a simple category method I added to NSImage:
    >
    > - (void) makeThreadSafe
    > {
    > [self setCachedSeparately:YES];
    > [self setCacheMode:NSImageCacheNever];
    > }
    >
    > I basically call [theImage makeThreadSafe] as quickly as possible
    > after creating instances of any NSImage in the app.
    >
    > Hope this helps, at least to know that you're not alone :) Perhaps
    > as a test you could try the above technique just to see if we're
    > talking about the same phenomenon.
    >
    > Daniel
    >
    >
    > On Dec 1, 2006, at 8:06 AM, j o a r wrote:
    >
    >>
    >> On 1 dec 2006, at 09.33, Hidetomo Katsura wrote:
    >>
    >>> FYI, this is a textbook case of an OS leak. you (or at least i)
    >>> can easily tell from the backtrace. you are only responsible for
    >>> releasing a reference that's returned by a method or function.
    >>> just file a bug and move on. if it's a significant leak, you have
    >>> to come up with a workaround.
    >>>
    >>> in this case, a reference returned by CGLCreateContext (or
    >>> allocate_context. it doesn't matter) is the leak, and it was
    >>> never returned to you since -[NSImage TIFFRepresentaion] only
    >>> returns an autoreleased NSData object (not a CGLCreateContext).
    >>> obviously, you can't directly release a reference returned by
    >>> CGLCreateContext since you don't even have access to it.
    >>>
    >>> in short, if you didn't create (allocate, copy, or retain) it,
    >>> it's not your leak.
    >>
    >>
    >> Based on the data presented in this thread so far I would not
    >> agree that your claim is necessarily true.
    >>
    >> ObjectAlloc shows backtraces to things that are allocated, but
    >> that's not the same thing as leaked.
    >> If the piece of data that is allocated in this backtrace is still
    >> live, that is most likely because its "owner" has not released /
    >> freed it yet. Note that the "leak event" is typically not the same
    >> thing as the allocation event!
    >>
    >> Presented with something that could be either [1] a memory leak in
    >> a framework, or [2] a memory leak in your code, you should always
    >> suspect your code - Unless the evidence to the contrary is
    >> overwhelming.
    >>
    >> In this case we don't even know what's being leaked - is it opaque
    >> malloced data? It should be of primary interest to see if the
    >> NSData instances returned from "-TIFFRepresentation" are leaked or
    >> not. This, and many other similar points of data, is still unknown.
    >>
    >> The best thing would of course be if this proposed leak could be
    >> isolated in a small sample program, and posed for others to have a
    >> look at.
    >>
    >> I'm not saying that you're necessarily wrong, only that I don't
    >> think we have enough information to assert that you're right.
    >>
    >> j o a r

    This is an interesting analysis. The app does manage NSImages from
    different threads, but only simple things like displaying them in
    NSImageViews (again, in the main thread). I call TIFFRepresentation
    since it's the only way I know to write the NSImage out to a file,
    but this is called in only one place, and the leak only occurs when I
    do that. I've implemented the category Daniel describes, but the leak
    still results. There must be some complex interactions going on
    because I can't reproduce the leak in a standalone multithreaded app.
    If this is a known bug, I guess I'll have to live with it for now,
    unless I can find another way to generate an image file from the
    NSImage.

    Roland
  • On 12/1/06, Roland Torres <newslists...> wrote:

    > I call TIFFRepresentation since it's the only way I know to write the NSImage out
    > to a file, but this is called in only one place, and the leak only occurs when I do
    > that. ... I guess I'll have to live with it for now, unless I can find another way to
    > generate an image file from the NSImage.

    How are you creating you NSImages? What is the source of their image data?

    NSImages are in a way an abstraction of a displayable image while the
    real image data in contained in an image rep which NSImage may have
    one or more of. If your images are bitmap based then look at using
    -[NSBitmapImageRep representationUsingType:properties:] to request an
    NSData instance with the bitmap data in a particular image format. You
    can get the image rep from the NSImage... however it isn't clear how
    you are creating your images so it isn't clear what reps they will
    contain. Also you can draw an image into a NSBitmapImageRep and then
    ask for the format of your choice... or use ImageIO (part of
    CoreImage).

    Also do you know what memory or object is leaking? It would help to
    figure out what it is... I have a feeling it is something related to
    the CGL context that is getting created in the process of converting
    the image data into TIFF format. I have seen some reports go by on the
    quartz and opengl lists of contexts created by CGLCreateContext then
    later destroyed by CGLDestroyContext resulting in a leak.

    -Shawn
  • On Dec 1, 2006, at 10:09 AM, Shawn Erickson wrote:

    > On 12/1/06, Roland Torres <newslists...> wrote:
    >
    >> I call TIFFRepresentation since it's the only way I know to write
    >> the NSImage out
    >> to a file, but this is called in only one place, and the leak only
    >> occurs when I do
    >> that. ... I guess I'll have to live with it for now, unless I can
    >> find another way to
    >> generate an image file from the NSImage.
    >
    > How are you creating you NSImages? What is the source of their
    > image data?
    >
    > NSImages are in a way an abstraction of a displayable image while the
    > real image data in contained in an image rep which NSImage may have
    > one or more of. If your images are bitmap based then look at using
    > -[NSBitmapImageRep representationUsingType:properties:] to request an
    > NSData instance with the bitmap data in a particular image format. You
    > can get the image rep from the NSImage... however it isn't clear how
    > you are creating your images so it isn't clear what reps they will
    > contain. Also you can draw an image into a NSBitmapImageRep and then
    > ask for the format of your choice... or use ImageIO (part of
    > CoreImage).
    >
    > Also do you know what memory or object is leaking? It would help to
    > figure out what it is... I have a feeling it is something related to
    > the CGL context that is getting created in the process of converting
    > the image data into TIFF format. I have seen some reports go by on the
    > quartz and opengl lists of contexts created by CGLCreateContext then
    > later destroyed by CGLDestroyContext resulting in a leak.

    Basically, I'm starting with a CIFilter. Then, (error checking
    removed for clarity):

      NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
      CIImage *ciImage = [filter valueForKey:@"outputImage"];
      NSCIImageRep *ciImageRep = [NSCIImageRep
    imageRepWithCIImage:ciImage];
      NSImage *nsImage = [[NSImage alloc]init];
      [nsImage setCachedSeparately:YES];
      [nsImage setCacheMode: NSImageCacheNever];
      [nsImage addRepresentation: ciImageRep];
      [self performSelectorOnMainThread:@selector(writeToDisk:)
                              withObject:nsImage
                          waitUntilDone:YES];
      [nsImage release];
      [pool release];
      . . .

    -(void)writeToDisk:(NSImage *)nsImage
    {
      // following steps done on main thread
      NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
      NSData *nsData=[nsImage TIFFRepresentation]; // <-- leaks here
      [nsData writeToFile:filespec atomically:NO];
      [pool release];
    }

    Is there an easier way to go from a CIImage to an image file? This
    seems quite a roundabout way.

    Roland
  • Roland Torres wrote:

    > Is there an easier way to go from a CIImage to an image file?

    Roland wants to writeout a CIImage and to do this he creates an
    NSCIImage
    and from this he gets data via -TIFFRepresentation.
    Troy Stephens gave a code example for a much easier way in:

    http://www.cocoabuilder.com/archive/message/cocoa/2005/5/18/136311

    Troy Stephens writes:

    > It's about the same amount of source code as what you had, but avoids
    > the unnecessary allocation of the intermediate "TIFFRepresentation"
    > data and NSImage.

        Heinrich

    --
    Heinrich Giesen
    <giesenH...>
  • On Dec 2, 2006, at 2:23 AM, Heinrich Giesen wrote:
    >
    > Roland Torres wrote:
    >
    >> Is there an easier way to go from a CIImage to an image file?
    >
    > Roland wants to writeout a CIImage and to do this he creates an
    > NSCIImage
    > and from this he gets data via -TIFFRepresentation.
    > Troy Stephens gave a code example for a much easier way in:
    >
    > http://www.cocoabuilder.com/archive/message/cocoa/2005/5/18/136311
    >
    > Troy Stephens writes:
    >
    >> It's about the same amount of source code as what you had, but avoids
    >> the unnecessary allocation of the intermediate "TIFFRepresentation"
    >> data and NSImage.

    Thanks, Heinrich (and Troy Stephens)! The Cocobuilder code segment
    works perfectly! It leaks 0 bytes. Plus, I get the option to write
    the image out in TIFF, JPG, BMP, PNG, and even GIF89a format.

    (FWIW, I can reproduce my original leak in the Cocobuilder code if I
    don't free the NSBitmapImageRep. This will cause it to accumulate VM
    at the exact same rate as using NSImage -TIFFRepresentation.
    Interesting.)

    Many thanks to all who have helped, both on-list and off-list!
    Roland
previous month november 2006 next month
MTWTFSS
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      
Go to today