Best way to periodically drain the autorelease pool on a long-running background thread?

此生再无相见时 提交于 2020-01-03 12:17:30

问题


In the developer documentation, it says:

If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically drain and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.

I was wondering what the best way to do this is. I have several methods I think would work, but don't know which is the "best". I currently have a method that start the thread and keeps it waiting for operations to perform:

- (void)startThread
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    accessoryRunLoop = [NSRunLoop currentRunLoop];

    //Add a dummy port to avoid exiting the thread due to no ports being found
    [accessoryRunLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

    while(accessoryThreadIsRunning)
    {
        //Keep the thread running until accessoryTheadIsRunning == NO
        [accessoryRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    [pool release];
}

My options I can think of are:

1) Add a counter in the while(accessoryThreadIsRunning) so that every 50 or 100 times it will drain the autorelease pool and create a new one.

2) Every time I perform a method in that thread (using performSelector: onThread:), I can create an autorelease pool and then release it at the end of the method.

3) Make a timer so that a pool is drained and then created periodically.

I think that option 1 is the best, but would like to know if there is a different way I should be doing this. Thanks!


回答1:


I'd start with dead simple and just create/drain the pool on every pass through the loop.

If it shows up during performance analysis as a bottle neck, fix it.

Keep it simple until analysis indicates complexity is required.


I just re-read your question and realized something entirely boneheaded in my answer. If you are running a run-loop it should be managing an autorelease pool automatically for you; it should create a pool at the top of the loop and drain it at the end of each pass through the loop.

You only need to cycle one yourself if you have other stuff going on outside of the runloop. Is that the case?

In any case, yes, the pattern is:

 while(...) {
    ... create pool ...
    ... do stuff ...
    ... drain pool ...
 }



回答2:


Drain it each time. As others have said draining an autorelease pool is cheap.

Moreover NOT draining it can be very costly. If you have enough stuff in your autorelease pool to cause paging you cause disk I/O, and disk I/O is literally thousands if not millions of times more costly then running a linked list calling release on stuff. (and on systems like iOS that don't have paging, lots of extra objects waiting to autorelease can cause memory low warnings, which might cause applications to be forced to exit, or the foreground application to go release a bunch of Nib views or something, then it then has to recreate later...or it might just force your application to exit).

Even if you don't use "enough" extra memory to cause low memory warnings or paging you will be running a larger staler list of items to drain. More memory accesses will be between your newest autorelease item and the oldest. There is a much greater chance that the oldest autorelease item is now farther away in the memory hierarchy, so your release may have cache misses vs. a L1 or L2 cache hit. So maybe 100 times more costly. Plus the memory you would have released (and might have been hot in the cache) might well have been reused by another object.

So doing the autorelease every 50 to 100 times might not even manage to be premature optimization.

Do one release per loop, and then if that shows as a bottleneck make it every X times, and make sure that makes it faster not slower.




回答3:


The main thread's run loop drains its pool on each pass, so it makes sense to do it on other threads too. If you choose to drain the pool only occasionally, you risk having a lot of autoreleased objects waiting to be deallocated for a long time. In fact, it depends on how much memory you can release on each pass of the run loop and how often you trigger the run loop. I always prefer to drain it on each pass just because it's easy and helps me keep the memory footprint as low as possible.




回答4:


The conventional way is, yes to keep a counter and drain every 50 or so times, but as bbum said, just start out with draining the pool every loop, and go from there. OR you could -init the objects that you need, and not create any autoreleased objects. (just stick clear of factory methods) Remember to -release all your objects, though.



来源:https://stackoverflow.com/questions/3807035/best-way-to-periodically-drain-the-autorelease-pool-on-a-long-running-background

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!