在长时间运行的后台线程上定期耗尽自动释放池的最佳方法?
在开发者文档中,它说:
如果您的应用程序或线程是长期存在的并且可能会生成大量自动释放对象,则您应该定期耗尽并创建自动释放池(就像应用程序工具包在主线程上所做的那样);否则,自动释放的对象会累积并且内存占用量会增加。但是,如果您的分离线程不进行 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 是最好的,但我想知道是否有其他方法可以做到这一点。谢谢!
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我将从非常简单的开始,只需在每次循环时创建/排出池。
如果它在性能分析过程中显示为瓶颈,请修复它。
保持简单,直到分析表明需要复杂性为止。
我刚刚重新阅读了你的问题,并意识到我的回答完全是愚蠢的。如果您正在运行运行循环,它应该自动为您管理自动释放池;它应该在循环的顶部创建一个池,并在每次循环结束时将其排干。
如果您在运行循环之外还有其他事情发生,您只需要自己循环一个。是这样吗?
无论如何,是的,模式是:
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:
每次都沥干。正如其他人所说,耗尽自动释放池的成本很便宜。
此外,不排空它可能的成本非常高。如果你的自动释放池中有足够的东西来导致分页,那么你就会导致磁盘 I/O,而磁盘 I/O 的成本实际上比运行一个调用释放东西的链表要贵数千倍甚至数百万倍。 (在像 iOS 这样没有分页功能的系统上,大量等待自动释放的额外对象可能会导致内存不足警告,这可能会导致应用程序被迫退出,或者前台应用程序释放一堆 Nib 视图之类的,然后它必须稍后重新创建...或者它可能只是强制您的应用程序退出)。
即使您没有使用“足够”的额外内存来导致内存不足警告或分页,您也将运行更大的陈旧项目列表来耗尽。更多的内存访问将在最新的自动释放项和最旧的自动释放项之间进行。最旧的自动释放项现在在内存层次结构中距离较远的可能性更大,因此您的释放可能会出现缓存未命中而不是 L1 或 L2 缓存命中的情况。所以成本可能高出100倍。另外,您将释放的内存(并且可能在缓存中很热)很可能已被另一个对象重用。
因此,每 50 到 100 次执行一次自动释放甚至可能无法实现过早优化。
每个循环执行一次发布,然后如果这显示为瓶颈,则每隔 X 次发布一次,并确保这使其更快而不是更慢。
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.
主线程的运行循环在每次传递时都会耗尽其池,因此在其他线程上执行此操作也是有意义的。如果您选择只是偶尔耗尽池,则可能会面临大量自动释放对象长时间等待释放的风险。事实上,这取决于每次运行循环可以释放多少内存以及触发运行循环的频率。我总是喜欢在每次传递时耗尽它,因为它很简单并且可以帮助我保持尽可能低的内存占用。
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.
传统的方法是,是的,保留一个计数器,每 50 次左右排水一次,但正如 bbum 所说,从每个循环排水池开始,然后从那里开始。或者您可以
-init
您需要的对象,而不创建任何自动释放的对象。 (只需远离工厂方法)不过,请记住-release
所有对象。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.