Notice (2018-05-24): bugzilla.xamarin.com is now in
Please join us on
Visual Studio Developer Community and in the
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
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.
Created attachment 23232 [details]
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
Memory consumption is constant in a range determined by max size of image.
Memory consumption continuously growth over time.
In an attachment the line `var cgimg2 = nsimg.CGImage;` causes memory leak.
Possible to workaround with:
internal static extern void CFRelease(System.IntPtr obj);
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:
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.
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.