2013-04-26 2 views
2

Контекст: У меня есть приложение для игры в iOS, которое использует GCD. Для приложения у меня три очереди: главная очередь, игровая логическая очередь (пользовательская серийная версия), физическая очередь (пользовательский серийный номер). Физическая очередь используется для моделирования физики, а Game Queue используется для логики игры. Поэтому для каждого обновления (каждые 1/60 секунды) каждая очередь выполняет свою соответствующую работу, а затем делится ею с другими очередями, планируя блоки в других очередях.Быстрый рост кучи с Grand Central Dispatch

Проблема:

С НОДОМ: Когда я играю уровень игры т.е. очередей делает какую-то работу, я вижу, очень быстрый рост в моей куче/распределении, что приводит к приложению сбоя из-за проблемы с памятью. Если я выйду из уровня и перейду в не-игровое представление, то есть очередь НЕ делает никакой работы, память медленно падает (занимает около 2 минут) и становится стабильной. В прикрепленном изображении пик на изображении находится непосредственно перед тем, как выйти из игрового уровня и выйти на улицу. После этого происходит постоянное падение памяти, поскольку объекты освобождаются.

Без НОД: Если отключить другие две очереди и запустить все на главной очереди то есть я устранить все параллелизм из моего кода, я не вижу какого-либо значительного роста кучи и игра работает просто отлично.

Уже изучили/пытался/исследовал в Интернете: У меня есть краткое понимание концепций блока захвата и блока копируется в кучу, но я не очень уверен. Насколько я понимаю, я не смог найти какой-либо такой объект в своем коде, так как когда я выхожу из уровня игры и перехожу в неигровой режим, все объекты, которые должны быть освобождены, освобождаются.

Вопросы:

  1. Приложение с НОД создает много блоков. Это хорошая практика для создания множества блоков?
  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

+1

Да, если вы планируете быстрее, чем обрабатываете, не отменяя вещи после определенного предела, вы увидите эту проблему. Постепенное уменьшение использования памяти - это ваше приложение, обрабатывающее материал, набитый в очередь, которая не была завершена. Простой тест состоит в том, чтобы предоставить вашим блокам уникальный идентификатор целого числа и регистрировать их по мере их обработки. Таким образом, вы можете видеть, какие части вы обрабатываете в какое время, и если они обрабатываются после того, как вы уходите. – Bergasms

+0

Ваши предложения о том, что задачи накапливаются, кажутся правильными, так как при дальнейшем анализе я обнаружил, что хвост (где память медленно сужается) выполняет мои задачи. Но я немного смущен о перераспределении, поскольку я использую CADisplayLink в основном потоке, чтобы запускать работу над каждой очередью. Поэтому я считаю, что это не может быть быстрее, чем 1/60 секунды. – praveen

ответ

3

Я видел такую ​​же память памяти вокруг очереди GCD, когда у меня была автоматическая кадрирование CADisplayLink каждые 60 секунд, но блок рендеринга кадра, который занял больше времени, чем завершение. Блоки будут накапливаться в очереди, и, как вы видите, у них есть некоторые накладные расходы, связанные с ними.

Майк Эш имеет a great writeup about this, где он демонстрирует последствия создания блоков обработки, а также способы облегчения части этого давления. Кроме того, это было освещено на недавней сессии WWDC по GCD, а также о том, как диагностировать это в Инструментах, но я не могу найти конкретную сессию прямо сейчас.

В моем случае я использовал что-то похожее на то, к чему пришел Майк, и я использую семафор отправки, чтобы предотвратить накопление блоков в памяти. Я описываю этот подход в this answer, а также код. Я использую семафор с максимальным количеством 1, а затем проверяю его перед отправкой нового блока. Если в последовательной очереди сидит другой блок этого типа, я запираюсь и не бросаю другого на кучу. Как только блок завершил выполнение, я уменьшаю счет семафора, чтобы добавить еще один.

Похоже, вам нужно что-то вроде этого, чтобы управлять добавлением новых блоков в ваши очереди, потому что вы захотите сбросить кадры с увеличением нагрузки в своей игре.

0

основной очередь имеет autorelease пул, который сливает в каждой итерации runloop

приуроченных очередь не Id думает .. по крайней мере NSThreads по умолчанию не

обернуть код очередь в @autoreleasepool

+0

Я уже завернул все, что делалось в очередях в пулах Autorelease. – praveen

0

Попробуйте использовать dispatch_async_f вместо dispatch_async. Это позволяет избежать копирования блока.

Смежные вопросы