Bug 57874 - Memory Leak when calling NSImage.CGImage
Summary: Memory Leak when calling NSImage.CGImage
Status: RESOLVED ANSWERED
Alias: None
Product: Xamarin.Mac
Classification: Desktop
Component: Runtime ()
Version: 3.4.0 (15.2)
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-06-30 01:16 UTC by Denis Rotanov
Modified: 2017-07-08 02:54 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
testcase (1.00 MB, application/x-zip-compressed)
2017-06-30 01:16 UTC, Denis Rotanov
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Denis Rotanov 2017-06-30 01:16:39 UTC
Created attachment 23232 [details]
testcase

When calling .CGImage property of NSImage instance memory is allocated and never freed.

Steps to Reproduce:
1. Create NSImage from data or from file
2. Access CGImage property of this NSImage instance.
3. repeat 1.2. for multiple NSImage

Expected result:
Memory consumption is constant in a range determined by max size of image.

Actual result:
Memory consumption continuously growth over time.

Testcase attached.
In an attachment the line `var cgimg2 = nsimg.CGImage;` causes memory leak.

Possible to workaround with:

[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
internal static extern void CFRelease(System.IntPtr obj);
Comment 1 Chris Hamons 2017-07-07 14:41:38 UTC
So this is not a leak in Xamarin.Mac, nor a bug, but I can understand why it would be confusing. 

As you discovered, the call in question causing issues is this:

var cgimg2 = nsimg.CGImage;

which if you dig into turns into a call to CGImageForProposedRect.

The header documentation for that API is:

The CGImageRef returned is guaranteed to live as long as the current autorelease pool.  The caller should not release the CGImage.  This is the standard Cocoa convention, but people may not realize that it applies to CFTypes.
 */
- (nullable CGImageRef)CGImageForProposedRect:(nullable NSRect *)proposedDestRect context:(nullable NSGraphicsContext *)referenceContext hints:(nullable NSDictionary<NSString *, id> *)hints NS_AVAILABLE_MAC(10_6) CF_RETURNS_NOT_RETAINED;

Note the "current autorelease pool" section. Unlike other APIs, we _do not_ control lifetime of these (large) objects.

This is trivial to work around however, by wrapping the core of your code (everything inside the while true loop with):

using (NSAutoreleasePool p = new NSAutoreleasePool ()) 
{
}

this will create an auto release pool and flush it. You can read about auto release pools here:

https://developer.apple.com/documentation/foundation/nsautoreleasepool

In most Xamarin.Mac code they are unnecessary, as Cocoa API calls generally set them up for you. However, since you are on your own "forever" thread, you must do it.
Comment 2 Denis Rotanov 2017-07-08 02:54:58 UTC
Thank you so much. Indeed this was not an obvious thing, hard to find a solution being neither familiar with objective c nor apple memory management patterns. I'll try to spread the knowledge.