在长时间运行的后台线程上定期耗尽自动释放池的最佳方法?

时间:2010-09-27 19:17:17

标签: objective-c multithreading autorelease

在开发人员文档中,它说:

如果你的应用程序或线程很长并且可能生成大量自动释放的对象,你应该定期排空并创建自动释放池(就像主要线程上的Application Kit一样);否则,自动释放的对象会累积,并且您的内存占用会增加。但是,如果您的分离线程没有进行Cocoa调用,则不需要创建自动释放池。

我想知道最好的办法是什么。我认为有几种方法可行,但不知道哪种方法是“最好的”。我目前有一个启动线程的方法,并让它等待执行操作:

- (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];
}

我能想到的选择是:

1)在while(accessoryThreadIsRunning)中添加一个计数器,这样每50或100次它将耗尽自动释放池并创建一个新的。

2)每次我在该线程中执行一个方法(使用performSelector:onThread :),我都可以创建一个自动释放池,然后在方法结束时释放它。

3)制作一个计时器,以便排空一个池,然后定期创建。

我认为选项1是最好的,但我想知道我应该采用不同的方式做到这一点。谢谢!

4 个答案:

答案 0 :(得分:4)

我从简单开始,只需在每次循环中创建/排出池。

如果它在性能分析中显示为瓶颈,请修复它。

保持简单直到分析表明需要复杂性。


我只是重新阅读了你的问题,并在我的回答中意识到完全愚蠢的事情。如果您正在运行一个运行循环,应该自动为您管理自动释放池;它应该在循环顶部创建一个池,并在每次循环结束时将其排出。

如果你有其他东西在runloop之外进行,你只需要自己循环一个。是这样的吗?

无论如何,是的,模式是:

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

答案 1 :(得分:2)

每次排水。正如其他人所说,自动释放池的耗尽很便宜。

此外,不要耗尽可以非常昂贵。如果您的自动释放池中有足够的内容导致分页,则会导致磁盘I / O,而磁盘I / O实际上是数千甚至数百万倍,然后运行链接列表调用发布的东西。 (在没有分页的iOS系统上,等待自动释放的大量额外对象可能导致内存低警告,这可能导致应用程序被迫退出,或者前台应用程序将释放一堆Nib视图或其他东西,然后它必须稍后重新创建......或者它可能只是强制你的应用程序退出)。

即使您没有使用“足够的”额外内存来导致内存不足警告或分页,您也会运行更大的staler列表来排空。您最新的自动释放项目与最旧的自动释放项目之间将进行更多内存访问。最旧的自动释放项目现在更大的可能性在内存层次结构中更远,因此您的版本可能有缓存未命中与L1或L2缓存命中。所以可能要贵100倍。此外,您可能已经释放的内存(可能在缓存中很热)很可能已被另一个对象重用。

因此,每50到100次进行自动释放可能甚至不会过早优化。

每个循环执行一次释放,然后如果显示为瓶颈,则每隔X次执行一次,并确保使其更快而不慢。

答案 2 :(得分:0)

主线程的运行循环在每次传递时都会耗尽其池,因此在其他线程上执行它也是有意义的。如果您选择仅偶尔排空池,则可能会有很多自动释放的对象等待长时间释放。实际上,它取决于运行循环的每次传递可以释放多少内存以及触发运行循环的频率。我总是喜欢在每次通过时将其耗尽,因为它很容易并且有助于我尽可能降低内存占用。

答案 3 :(得分:0)

传统的方法是,保持一个计数器并且每50次消耗一次,但是正如bbum所说的那样,只需从每个循环中排出水池开始,然后从那里开始。或者你可以-init你需要的对象,而不是创建任何自动释放的对象。 (只是避开工厂方法)但请记住-release所有对象。