Как избежать фрагментации кучи в iOS
Наше приложение создает много мелких объектов во время работы. В основном это сводится к объектам NSString и NSNumber с автоматическим выпуском. Поскольку приложение предназначено для запуска "24/7" в фоновом режиме, фрагментация кучи становится большой проблемой.
Каковы методы, чтобы избежать этого без полной реструктуризации программы.
Я думал: - пулы объектов, которые возвращали бы объект в пул после окончательного выпуска, но тогда объекты должны быть изменяемыми. (Будет ли NSMuttableString сам по себе вызывать фрагментацию кучи?)
Как другие решают эти проблемы?
РЕДАКТИРОВАТЬ: Вот как у меня есть подозрения к фрагментации памяти. Посмотрите на rpages и [vm-pageshortage]
eIncident Identifier: 81E87769-8E16-4439-AFFA-6D077E01E5ED
CrashReporter Key: 96235931c31c6b92a16f5c1b1e4cb363a3d18a67
Hardware Model: iPhone4,1
OS Version: iPhone OS 7.0.4 (11B554a)
Kernel Version: Darwin Kernel Version 14.0.0: Fri Sep 27 23:00:48 PDT 2013; root:xnu-2423.3.12~1/RELEASE_ARM_S5L8940X
Date: 2013-12-13 22:43:36 -0800
Time since snapshot: 1582 ms
Free pages: 1105
Active pages: 3668
Inactive pages: 2035
Speculative pages: 46
Throttled pages: 100120
Purgeable pages: 0
Wired pages: 22159
File-backed pages: 5400
Anonymous pages: 349
Compressions: 0
Decompressions: 0
Compressor Size: 0
Uncompressed Pages in Compressor: 0
Largest process: Argus
Processes
Name <UUID> rpages recent_max fds [reason] (state)
Facebook <979b9707d85a31df94b986d91d8c3ce7> 2368 2368 100 [vm-pageshortage] (resume)
MobileSMS <339505ebbbc4301e87379b095a38ba13> 1448 1448 100 [vm-pageshortage] (background)
MobileMail <b3574f4bded1315cb2e50e5de205be48> 1575 1575 100 [vm-pageshortage] (resume) (continuous)
tccd <1fea8c5a71943151b5cd304c7eb0fd8c> 198 198 100 [vm-pageshortage] (daemon)
kbd <be2d64e41bf43e48a09a23fb129eb0b4> 739 739 100 [vm-pageshortage] (daemon)
librariand <15fb21b24e823e158caed9f9e9d8b87a> 299 299 100 [vm-pageshortage] (daemon)
MobilePhone <10e2242652423ae28f278a807a0d6384> 1852 1852 200 [vm-pageshortage] (continuous)
CVMServer <f26614f7fef63e2faa518272f0fc600a> 96 96 200 [vm-pageshortage] (daemon)
Argus <d214b453a3453121a8495d5c8eba80fd> 51299 51299 100 [vm-pageshortage] (location) (frontmost) (resume)
identityservices <18cc20db2e4739a782cc8e38e03eff52> 398 398 100 (daemon)
wifid <a5cf99e5a0f032a69bc2f65050b44291> 652 652 25 (daemon)
syslogd <6539f4cf4dcf34daadf1d99991926680> 140 140 50 (daemon)
powerd <0a253ac2a99236809422214be1700bc0> 126 126 100 (daemon)
vmd <93cffd22b64631afa08a42f6a85e1f33> 297 297 100 (daemon)
imagent <bef102e1faef39209926fb25f428a71e> 438 438 100 (daemon)
1 ответ
Один из способов решения этой проблемы - найти виновника "90%", который вызывает проблему фрагментации. Вероятно, в вашем коде патологическое состояние, которое вызывает эффект швейцарского сыра.
Предварительные действия
Само собой разумеется, что вы должны сначала убедить себя, что интенсивное использование страниц происходит из-за фрагментации, а не из-за утечки памяти.:-)
Если вы еще этого не сделали, использование приложения "Инструменты" в XCode - отличный способ наблюдать за тем, как ваша программа распределяет память в симуляторе iOS. Использование инструментов Allocations и Leaks позволяет вам записывать каждое распределение объектов и malloc(), которые делает ваш код, вместе с удобными временными метками. (И бесплатно, инструмент " Утечки", он покажет вам циклы на вашей карте объектов, если ARC не освобождает память, как следует.)
Обычно инструменты отслеживают память, которая все еще используется. Вы можете выбрать опцию " Создано и уничтожено" на панели конфигурации " Выделения", чтобы отслеживать все, что отображается во всплывающем окне прямо над сводной информацией.
Существует также инструмент выделения страниц виртуальной машины, который может пролить свет на вашу проблему.
Возможные средства правовой защиты
Как только вы определили виновника, вы можете реструктурировать свой код, чтобы предотвратить патологическое состояние. Или, если реструктуризация слишком дорога, вы можете смягчить последствия этого условия, повторно используя память.
Обычно, когда анализ показывает, что выделение и освобождение объекта вызывает дыры или конкретный объект выделяется / освобождается с пугающей частотой, я беру преступника-нарушителя и превращаю его в "пул" объектов.
Это может быть так же просто, как сохранить объекты в NSMutableArray и нажать и вытолкнуть их по мере необходимости.
Конечно, вы можете захотеть что-то более сложное, которое инициализирует объекты при получении, заполняет пул готовыми объектами и автоматически заполняет объекты, когда оно пустое или заканчивается. В iOS вы также можете сократить пул, если слишком много объектов собрано или получено предупреждение о нехватке памяти.
Приятно то, что это может быть очень общий код, который вы можете использовать вечно.:-)
Примечание
Вы упомянули проблему нежелания делать объекты изменяемыми. В некоторых случаях (например, NSString) это может помочь с эффективностью, если вы знаете (заранее) требования к пространству и манипуляции, которые вам нужно будет выполнить со строкой. Таким образом, вы можете заранее указать, сколько места вам понадобится, и обработать строку "на месте", что сократит накладные расходы. (То, что NSString делает под капотом - это совсем другая статья.:-)