Контекст: У меня есть приложение для игры в iOS, которое использует GCD. Для приложения у меня три очереди: главная очередь, игровая логическая очередь (пользовательская серийная версия), физическая очередь (пользовательский серийный номер). Физическая очередь используется для моделирования физики, а Game Queue используется для логики игры. Поэтому для каждого обновления (каждые 1/60 секунды) каждая очередь выполняет свою соответствующую работу, а затем делится ею с другими очередями, планируя блоки в других очередях.Быстрый рост кучи с Grand Central Dispatch
Проблема:
С НОДОМ: Когда я играю уровень игры т.е. очередей делает какую-то работу, я вижу, очень быстрый рост в моей куче/распределении, что приводит к приложению сбоя из-за проблемы с памятью. Если я выйду из уровня и перейду в не-игровое представление, то есть очередь НЕ делает никакой работы, память медленно падает (занимает около 2 минут) и становится стабильной. В прикрепленном изображении пик на изображении находится непосредственно перед тем, как выйти из игрового уровня и выйти на улицу. После этого происходит постоянное падение памяти, поскольку объекты освобождаются.
Без НОД: Если отключить другие две очереди и запустить все на главной очереди то есть я устранить все параллелизм из моего кода, я не вижу какого-либо значительного роста кучи и игра работает просто отлично.
Уже изучили/пытался/исследовал в Интернете: У меня есть краткое понимание концепций блока захвата и блока копируется в кучу, но я не очень уверен. Насколько я понимаю, я не смог найти какой-либо такой объект в своем коде, так как когда я выхожу из уровня игры и перехожу в неигровой режим, все объекты, которые должны быть освобождены, освобождаются.
Вопросы:
- Приложение с НОД создает много блоков. Это хорошая практика для создания множества блоков?
- При работе с инструментами я обнаружил, что объекты, которые быстро получают выделение, но не освобождаются, относятся к категории Malloc 48. Ответственная библиотека для этих объектов - libsystem_blocks.dylib, а ответственный вызывающий - _Block_copy_internal. Эти объекты медленно освобождаются, когда я выхожу из уровня игры, то есть когда очереди перестают выполнять какую-либо работу. Тем не менее, освобождение происходит очень медленно и занимает около 2 минут, чтобы полностью очистить. Можно ли ускорить эту очистку? Мое подозрение в том, что объекты продолжают накапливаться, а затем приводят к сбою памяти.
Любые идеи относительно того, что может происходить?
Заранее спасибо.
На основании предложений нижеприведенных комментариев я написал следующий тестовый код. Я в основном заплатил вызов из 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, как упоминалось в предыдущем сообщении. Но удивительно, что в этом коде память никогда не падает до своего начального уровня.Вывод инструментов выглядит следующим образом:
Что не так с этим кодом? Любые идеи помогут.
Как мы можем знать что-либо, не видя какого-либо кода? Во всяком случае, я предполагаю, что вы планируете вещи быстрее, чем 1/60 секунды, чтобы они накапливались все больше и больше. Когда вы делаете это в главной очереди, он синхронизируется с частотой обновления, поэтому у него нет шанса навалиться. – borrrden
Да, если вы планируете быстрее, чем обрабатываете, не отменяя вещи после определенного предела, вы увидите эту проблему. Постепенное уменьшение использования памяти - это ваше приложение, обрабатывающее материал, набитый в очередь, которая не была завершена. Простой тест состоит в том, чтобы предоставить вашим блокам уникальный идентификатор целого числа и регистрировать их по мере их обработки. Таким образом, вы можете видеть, какие части вы обрабатываете в какое время, и если они обрабатываются после того, как вы уходите. – Bergasms
Ваши предложения о том, что задачи накапливаются, кажутся правильными, так как при дальнейшем анализе я обнаружил, что хвост (где память медленно сужается) выполняет мои задачи. Но я немного смущен о перераспределении, поскольку я использую CADisplayLink в основном потоке, чтобы запускать работу над каждой очередью. Поэтому я считаю, что это не может быть быстрее, чем 1/60 секунды. – praveen