2013-04-26 28 views
2

上下文:我有一个使用GCD的iOS游戏应用程序。对于应用程序,我有三个队列:主队列,游戏逻辑队列(自定义串行),物理队列(自定义串行)。物理队列用于进行物理模拟,Game Queue用于执行游戏逻辑。因此,对于每次更新(每1/60秒),每个队列都会执行相应的工作,然后通过调度其他队列上的块与其他队列共享。快速堆增长与大中央调度

问题:

随着GCD:当我玩游戏的水平,即在队列中做了一些工作,I /看到一个非常快速的增长,我的堆分配导致应用程序由于崩溃内存问题。如果我退出关卡并进入非游戏视图,即队列没有做任何工作,内存就会慢慢下降(大约需要2分钟)并变得稳定。在附图中,图像中的高峰就在我退出游戏关卡之前并出现在外面。之后,随着对象被释放,内存会持续下降。

没有GCD:如果我禁用其他两个队列,并运行在主队列即我消除我的代码全部并发性的一切,我看不出有任何显著堆增长和游戏作品就好了。

已经学/试过/研究在互联网上: 我有块捕获和块的概念的简要理解被复制到堆,但我不是很确定。据我的理解,在我的代码中,我一直无法找到任何这样的对象,因为当我退出游戏级别并进入非游戏视图时,所有将被释放的对象都将被释放。

问题:

  1. 与GCD的应用创造了很多块。创建大量的块是否是一种很好的做法?
  2. 在正在运行的乐器上,我发现快速获得分配但未获得发布的对象属于Malloc 48类。这些对象的负责库是libsystem_blocks.dylib,负责调用者是_Block_copy_internal。一旦我退出游戏级别,即队列停止执行任何工作时,这些对象就会慢慢被释放。但是,释放非常缓慢,需要大约2分钟才能完全清理。有没有什么办法可以加速这种清理?我的怀疑是对象不断堆积,然后导致内存崩溃。

关于可能发生什么的任何想法?

在此先感谢。 enter image description here

根据以下评论的建议,我写了下面的测试代码。我基本上是从一个CADisplayLink安排了一个回叫,然后在回拨中我将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相同的堆增长。但令人惊讶的是,在这段代码中,内存永远不会回落到初始水平。该仪器输出如下:

enter image description here

什么是错的代码?任何想法都会有帮助。

+1

我们怎么可能知道什么都没有看到一些代码?无论如何,我的猜测是,你正在安排比1/60秒更快的事情,所以他们越来越多地堆积起来。当您在主队列中执行此操作时,它与刷新率同步,因此无法堆积。 – borrrden 2013-04-26 06:38:01

+1

是的,如果你的调度速度比你在处理某个限制之后没有取消的时间快,你会看到这个问题。内存使用的逐渐减少是您的应用程序处理堆积在未完成的队列中的内容。一个简单的测试是给你的块定义唯一的整数ID,并在处理它们时记录它们。这样你就可以看到你在什么时候处理了哪些部分,以及在你退出后是否正在处理它们。 – Bergasms 2013-04-26 06:48:23

+0

你的建议,任务堆积似乎是正确的,因为在进一步的分析中,我发现尾巴(内存逐渐减少)正在执行我的任务。但是我对调度过度有点困惑,因为我在主线程上使用CADisplayLink来触发每个队列的工作。所以我认为不能超过1/60秒。 – praveen 2013-04-26 10:44:06

回答

3

队列中我已经看到了这个相同的排序围绕GCD队列存储器积聚时,我有一个CADisplayLink发射第二的每60位,但是花了比更长的时间来完成一帧渲染块。块将堆积在队列中,正如你所看到的,它们有一些与它们相关的开销。

迈克水曲柳具有a great writeup about this,在那里他展示了建立处理模块,以一定程度缓解这种压力的方式沿的后果。此外,WWDC最近的一次关于GCD的会议以及如何在乐器中对此进行诊断的内容包括了这一点,但目前我找不到具体的会议。

在我的情况,我结束了使用类似于迈克来到东西,我用的调度信号,以防止内存块的积累。我在this answer和代码中描述了这种方法。我所做的是使用最大计数为1的信号量,然后在分派新块之前检查它。如果另一个类型的块位于串行队列中,则我保释并且不要把另一个块放在堆上。一旦一个块完成执行,我减少信号计数,因此可以添加另一个。

这听起来像你需要这样的管理增加新的模块到你的队列,因为你会希望能够删除帧作为负荷增大时,你的游戏。

0

主队列具有在该runloop

并发队列不会标识想到的每一次迭代排水自动释放池..在默认情况下,至少NSThreads不

包装你的代码在@autoreleasepool

+0

我已经在Autorelease池中的队列中包装了所有的东西。 – praveen 2013-04-26 10:35:36

0

尝试使用dispatch_async的dispatch_async_f代替。它避免了块复制。