大中央调度快速堆积增长
背景:我有一个使用GCD的iOS游戏应用程序。 对于应用程序,我有三个队列:主队列,游戏逻辑队列(自定义串行),物理队列(自定义串行)。 物理队列是用来做物理模拟和游戏队列是用来做游戏的逻辑。 因此,对于每次更新(每1/60秒),每个队列执行相应的工作,然后通过在其他队列上调度块来与其他队列共享。
问题:
使用GCD:当我玩游戏级别,即队列正在做一些工作时,我看到我的堆/分配非常迅速的增长,导致应用程序崩溃,由于内存问题。 如果我退出关卡并进入非游戏视图,即队列没有任何工作,内存缓慢下降(大约2分钟),并变得稳定。 在附图中,图片中的高峰就在我离开游戏关卡之前出现在外面。 之后,随着对象被释放,内存会有一个稳定的下降。
没有GCD:如果我禁用其他两个队列并在主队列上运行所有内容,即从代码中消除所有并发,我看不到任何显着的堆增长,游戏运行良好。
已经在互联网上研究/尝试过/研究过:我对块捕获和块被复制到堆的概念有一个简单的理解,但是我不太确定。 就我的理解而言,我在代码中找不到任何这样的对象,因为当我退出游戏级别并进入非游戏视图时,所有预期将被释放的对象都将被释放。
问题:
- 与GCD的应用程序创build了很多块。 创build大量的块是一个好习惯吗?
- 在正在运行的乐器上,我发现快速分配但没有发布的对象是Malloc 48类。这些对象的负责任的库是libsystem_blocks.dylib,负责的调用者是_Block_copy_internal。 一旦我退出游戏级别,即队列停止执行任何工作,这些对象就会慢慢地被释放。 但是,释放非常缓慢,大约需要2分钟才能完全清理。 有没有什么办法可以加快清理呢? 我的怀疑是物体不断堆积,然后导致内存崩溃。
任何想法可能会发生什么?
提前致谢。
根据以下评论的build议,我写了下面的testing代码。 我基本上从一个CADisplayLink调度回来,然后在callback我排定5000个块到自定义队列。
// In a simple bare-bones view controller template I wrote the following code - (void)viewDidLoad { [super viewDidLoad]; self.objDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(loop:)]; [self.objDisplayLink setFrameInterval:1/60]; [self.objDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)loop:(CADisplayLink*)lobjDisplayLink { static int lintNumBlocks = 0; if (lintNumBlocks < 5000) { dispatch_async(self.testQueueTwo, ^{ @autoreleasepool { NSLog(@"Block Number ; %d", lintNumBlocks); int outerIndex = 1000; while (outerIndex--) { NSLog(@"Printing (%d, %d)", outerIndex, lintNumBlocks); } dispatch_async(dispatch_get_main_queue(), ^{ @autoreleasepool { NSString* lstrString = [NSString stringWithFormat:@"Finished Block %d", lintNumBlocks]; self.objDisplayLabel.text = lstrString; } }); } }); lintNumBlocks++; } else { self.objDisplayLabel.text = @"Finished Running all blocks"; [self.objDisplayLink invalidate]; } }
如上一篇文章中提到的,这个代码也给GCD带来了与GCD相同的堆增长。 但令人惊讶的是,在这个代码中,内存永远不会回落到初始水平。 仪器输出如下:
这段代码有什么问题? 任何想法都会有帮助。
当我每隔60秒就有一次CADisplayLink触发时,我已经看到了与GCD队列相同的内存堆积,但是花费比这更长的时间完成的帧渲染块。 块将堆积在队列中,正如你看到他们有一些与他们相关的开销。
Mike Ash对此有一个很好的描述 ,在这里他展示了build立加工模块的后果,以及减轻这些压力的方法。 另外,最近WWDC的一个关于GCD的会议也讨论了这个问题,以及如何在文书中诊断这个问题,但是我现在找不到具体的会议。
在我的情况下,我最终使用了类似于Mike到达的东西,并且使用调度信号来防止块在内存中累积。 我在这个答案中描述了这个方法,还有代码。 我所做的是使用一个最大计数为1的信号量,然后在调度一个新块之前检查它。 如果另一个types的块正在串行队列中,我保释,不要把另一个块放在一堆。 一旦一个块完成执行,我减less信号计数,所以可以添加另一个。
这听起来像你需要这样的事情来pipe理添加新的块到你的队列,因为你会希望能够随着你的游戏中的负载增加而丢帧。
主队列有一个自动释放池,在runloop的每一次迭代中都被排空
并发队列不认为..至lessNSThreads默认情况下不
将你的代码包装在@autoreleasepool的队列中
尝试使用dispatch_async_f而不是dispatch_async。 它避免了块复制。