Strange Memory Patterns
MacOS Sierra
Xcode 8.1
Build SDK 10.9
Deployment Target 10.6
Sandor
On Apr 24, 2019, at 9:41 PM, Sandor Szatmari <admin.szatmari.net@...> wrote:
We have an app that handles incoming server connections. For each connection it opens a window that shows the activity of the connection. When the client is done the window closes. When the activity happens while the app is in the background the application does not release memory and appears to leak. However, when I bring the app to the foreground the backlog of memory is released. It looks like auto release pools that aren’t popped until the app is foregrounded. I can track that down, but I thought I’d ask if anyone could identify this as a known issue before going down that path.
MacOS Sierra
Xcode 8.1
Build SDK 10.9
Deployment Target 10.6
Sandor
Sandor
On Apr 24, 2019, at 16:44, Alex Zavatone via Groups.Io <zav@...> wrote:
What background options have you enabled in the app? I admit that I am unfamiliar with Mac policy on background apps and only ask with iOS background.On Apr 24, 2019, at 9:41 PM, Sandor Szatmari <admin.szatmari.net@...> wrote:
We have an app that handles incoming server connections. For each connection it opens a window that shows the activity of the connection. When the client is done the window closes. When the activity happens while the app is in the background the application does not release memory and appears to leak. However, when I bring the app to the foreground the backlog of memory is released. It looks like auto release pools that aren’t popped until the app is foregrounded. I can track that down, but I thought I’d ask if anyone could identify this as a known issue before going down that path.
MacOS Sierra
Xcode 8.1
Build SDK 10.9
Deployment Target 10.6
Sandor
Issue 1: GCD callbacks are part of an autorelease pool, but you can't rely on it getting drained in a reliable manner, so wrap your callbacks with your own explicit pool.
Issue 2: Background apps do not seem to always have their main thread autorelease pools drained reliably. A snippet of code I've posted a few times over the years:
On Apr 25, 2019, at 10:44 AM, Jonathan Taylor <jonathan.taylor@...> wrote:This sounds very similar to problems I have encountered myself.
Issue 1: GCD callbacks are part of an autorelease pool, but you can't rely on it getting drained in a reliable manner, so wrap your callbacks with your own explicit pool.
Issue 2: Background apps do not seem to always have their main thread autorelease pools drained reliably. A snippet of code I've posted a few times over the years:
// Create a periodic timer that "tickles" the main event loop to drain autorelease pools.// Response from cocoa-dev discussion was that:// This is a long-standing problem with AppKit. According to the documentation,// "The Application Kit creates an autorelease pool on the main thread at the// beginning of every cycle of the event loop, and drains it at the end, thereby// releasing any autoreleased objects generated while processing an event."// However, this is somewhat misleading. The "end" of the event loop cycle is// immediately before the beginning. Thus, for example, if your app is in the background// and not receiving events, then the autorelease pool will not be drained. That's why// your memory drops significantly when you click the mouse or switch applications.[JDispatchTimer allocRepeatingTimerOnQueue:dispatch_get_main_queue() atInterval:5.0 withHandler:^{NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:[NSDate timeIntervalSinceReferenceDate] windowNumber:0 context:nil subtype:0 data1:0 data2:0];[NSApp postEvent:event atStart:YES];}];
In terms of my own issue, the work items I'm dealing with are GUI-related so their natural home is on the main queue. I don't know if it's possible to have a dedicated named queue that "lives" on the main thread, and whether that would solve anything here, but it's not something I've tried.
double delayInSeconds = 2.0;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0,
0,
dispatch_get_main_queue());
void (^memorySentinel)(void) = ^()
{
NSLog( @"I'm firing!" );
NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
location:NSZeroPoint
modifierFlags:0
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:0
context:nil
subtype:0
data1:0
data2:0];
[NSApp postEvent:event
atStart:YES];
};
if (timer)
{
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC),
delayInSeconds * NSEC_PER_SEC,
(1ull * NSEC_PER_SEC) / 10);
dispatch_source_set_event_handler(timer, memorySentinel);
dispatch_resume(timer);
}
else
{
// tell someone?
}
This sounds very similar to problems I have encountered myself.
Issue 1: GCD callbacks are part of an autorelease pool, but you can't rely on it getting drained in a reliable manner, so wrap your callbacks with your own explicit pool.
Issue 2: Background apps do not seem to always have their main thread autorelease pools drained reliably. A snippet of code I've posted a few times over the years:
// Create a periodic timer that "tickles" the main event loop to drain autorelease pools.// Response from cocoa-dev discussion was that:// This is a long-standing problem with AppKit. According to the documentation,// "The Application Kit creates an autorelease pool on the main thread at the// beginning of every cycle of the event loop, and drains it at the end, thereby// releasing any autoreleased objects generated while processing an event."// However, this is somewhat misleading. The "end" of the event loop cycle is// immediately before the beginning. Thus, for example, if your app is in the background// and not receiving events, then the autorelease pool will not be drained. That's why// your memory drops significantly when you click the mouse or switch applications.[JDispatchTimer allocRepeatingTimerOnQueue:dispatch_get_main_queue() atInterval:5.0 withHandler:^{NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:[NSDate timeIntervalSinceReferenceDate] windowNumber:0 context:nil subtype:0 data1:0 data2:0];[NSApp postEvent:event atStart:YES];}];