Максимальный бюджет памяти приложения ios
Я работаю над игрой ios, которая нацелена как минимум на 3gs. Мы используем HD-ресурсы для устройств отображения сетчатки (iphone 4, ipod touch 4th gen).
Что касается памяти, Ipod Touch 4-го поколения представляется нам наиболее ограничивающим устройством, поскольку он имеет такой же объем ОЗУ (256 по сравнению с 512 в Iphone 4), что и 3gs, но мы используем HD-ресурсы на нем. Раньше приложение зависало при попытке загрузить 100-110 МБ оперативной памяти, но теперь, когда у нас осталось 70 МБ, у нас никогда не было проблем с загрузкой.
После долгих поисков, кажется, нет официального жесткого ограничения, так как же нам узнать, какой бюджет памяти использовать для безопасности? Мы хотим предоставить художникам бюджет, который они могут использовать, не беспокоясь о памяти для каждой карты.
11 ответов
Я думаю, что вы ответили на свой собственный вопрос: старайтесь не выходить за пределы 70 Мб, однако это действительно зависит от многих вещей: какую версию iOS вы используете (не SDK), сколько приложений работает в фоновом режиме, какая именно память вы используете и т. д.
Просто избегайте мгновенных всплесков памяти (например, вы используете 40 МБ ОЗУ, а затем выделяете еще 80 МБ для некоторых коротких вычислений). В этом случае iOS немедленно убьет ваше приложение.
Вы также должны рассмотреть ленивую загрузку активов (загружайте их только тогда, когда вам действительно нужно, а не заранее).
Результаты тестирования с помощью утилиты Split написал (ссылка в его ответе):
устройство: (количество аварий / общее количество / процент от общего)
- iPad1: 127 МБ / 256 МБ / 49%
- iPad2: 275 МБ / 512 МБ / 53%
- iPad3: 645 МБ / 1024 МБ / 62%
- iPad4: 585 МБ / 1024 МБ / 57% (iOS 8.1)
- iPad Mini 1-го поколения: 297 МБ / 512 МБ / 58%
- Сетчатка iPad Mini: 696 МБ / 1024 МБ / 68% (iOS 7.1)
- iPad Air: 697 МБ / 1024 МБ / 68%
- iPad Air 2: 1383 МБ / 2048 МБ / 68% (iOS 10.2.1)
- iPad Pro 9,7": 1395 МБ / 1971 МБ / 71% (iOS 10.0.2 (14A456))
- iPad Pro 10,5”: 3057/4000/76% (iOS 11 бета4)
- iPad Pro 12,9 ”(2015): 3058/3999/76% (iOS 11.2.1)
- iPad Pro 12,9” (2017): 3057/3974/77% (iOS 11 бета4)
- iPad Pro 11.0 ”(2018): 2858/3769/76% (iOS 12.1)
- iPad Pro 12,9 ”(2018): 4598/5650/81% (iOS 12.1)
- iPod touch 4-го поколения: 130 МБ / 256 МБ / 51% (iOS 6.1.1)
- iPod touch 5-го поколения: 286 МБ / 512 МБ / 56% (iOS 7.0)
- iPhone4: 325 МБ / 512 МБ / 63%
- iPhone4s: 286 МБ / 512 МБ / 56%
- iPhone5: 645 МБ / 1024 МБ / 62%
- iPhone5s: 646 МБ / 1024 МБ / 63%
- iPhone6: 645 МБ / 1024 МБ / 62% (iOS 8.x)
- iPhone6 +: 645 МБ / 1024 МБ / 62% (iOS 8.x)
- iPhone6s: 1396 МБ / 2048 МБ / 68% (iOS 9.2)
- iPhone6s +: 1392 МБ / 2048 МБ / 68% (iOS 10.2.1)
- iPhoneSE: 1395 МБ / 2048 МБ / 69% (iOS 9.3)
- iPhone7: 1395/2048 МБ / 68% (iOS 10.2)
- iPhone7 +: 2040 МБ / 3072 МБ / 66% (iOS 10.2.1)
- iPhone X: 1392/2785/50% (iOS 11.2.1)
- iPhone XS Max: 2039/3735/55% (iOS 12.1)
Я создал небольшую утилиту, которая пытается выделить как можно больше памяти для сбоя и записывает, когда произошли предупреждения и сбой памяти. Это помогает выяснить, каков бюджет памяти для любого устройства iOS.
В моем приложении пользовательский опыт лучше, если используется больше памяти, поэтому я должен решить, действительно ли я должен освободить всю память, которую я могу в didReceiveMemoryWarning
, Судя по ответу Сплита и Джаспера Пола, использование максимально 45% общей памяти устройства является безопасным порогом (спасибо, ребята).
В случае, если кто-то хочет посмотреть на мою фактическую реализацию:
#import "mach/mach.h"
- (void)didReceiveMemoryWarning
{
// Remember to call super
[super didReceiveMemoryWarning];
// If we are using more than 45% of the memory, free even important resources,
// because the app might be killed by the OS if we don't
if ([self __getMemoryUsedPer1] > 0.45)
{
// Free important resources here
}
// Free regular unimportant resources always here
}
- (float)__getMemoryUsedPer1
{
struct mach_task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS)
{
float used_bytes = info.resident_size;
float total_bytes = [NSProcessInfo processInfo].physicalMemory;
//NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
return used_bytes / total_bytes;
}
return 1;
}
Свифт (основываясь на этом ответе):
func __getMemoryUsedPer1() -> Float
{
let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
let name = mach_task_self_
let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)
let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
let info = infoPointer.move()
infoPointer.dealloc(1)
if kerr == KERN_SUCCESS
{
var used_bytes: Float = Float(info.resident_size)
var total_bytes: Float = Float(NSProcessInfo.processInfo().physicalMemory)
println("Used: \(used_bytes / 1024.0 / 1024.0) MB out of \(total_bytes / 1024.0 / 1024.0) MB (\(used_bytes * 100.0 / total_bytes)%%)")
return used_bytes / total_bytes
}
return 1
}
Разветвив репозиторий SPLITS, я построил один для тестирования памяти iOS, который можно выделить для сегодняшнего расширения.
https://github.com/harsh62/iOSMemoryBudgetTestForExtension
Ниже приводится результат, который я получил в iPhone 5s
Память Предупреждение на 10 МБ
Приложение разбилось на 12 МБ
Таким образом, Apple просто позволяет любым расширениям работать с их полным потенциалом.
Начиная с iOS13, существует поддерживаемый Apple способ запроса с помощью
#include <os/proc.h>
size_t os_proc_available_memory(void)
Представлено здесь:https://developer.apple.com/videos/play/wwdc2019/606/
Примерно мин 29-иш.
Изменить: добавление ссылки на документацию https://developer.apple.com/documentation/os/3191911-os_proc_available_memory?language=objc
Вы должны смотреть сессию 147 из видео сессий WWDC 2010. Это "Продвинутая оптимизация производительности на iPhone OS, часть 2".
Есть много хороших советов по оптимизации памяти.
Вот некоторые из советов:
- Использовать вложенные
NSAutoReleasePool
s, чтобы убедиться, что использование памяти не увеличивается. - использование
CGImageSource
при создании миниатюр из больших изображений. - Реагируйте на предупреждения о недостатке памяти.
- (float)__getMemoryUsedPer1
{
struct mach_task_basic_info info;
mach_msg_type_number_t size = MACH_TASK_BASIC_INFO;
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS)
{
float used_bytes = info.resident_size;
float total_bytes = [NSProcessInfo processInfo].physicalMemory;
//NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
return used_bytes / total_bytes;
}
return 1;
}
Если вместо MACH_TASK_BASIC_INFO вы будете использовать TASK_BASIC_INFO_COUNT, вы получите
kerr == KERN_INVALID_ARGUMENT (4)
Я создал еще один список, отсортировав список Jaspers по оперативной памяти устройства (я провел собственные тесты с помощью инструмента Split и исправил некоторые результаты - проверьте мои комментарии в ветке Jaspers).
ОЗУ устройства: процентное расстояние до сбоя
- 256 МБ: 49% - 51%
- 512 МБ: 53% - 63%
- 1024 МБ: 57% - 68%
- 2048 МБ: 68% - 69%
- 3072 МБ: 66%
- 4096 МБ: 77%
- 6144 МБ: 81%
Особые случаи:
- iPhone X (3072 МБ): 50%
- iPhone XS / XS Max (4096 МБ): 55%
- iPhone XR (3072MB): не тестировался (пожалуйста, опишите значения сбоя в комментариях)
ОЗУ устройства можно легко прочитать:
[NSProcessInfo processInfo].physicalMemory
Исходя из моего опыта, можно безопасно использовать 45% для устройств с 1 ГБ, 50% для устройств с 2/3 ГБ и 55% для устройств с 4 ГБ. Процент для macOS может быть немного больше.
Работая со многими ответами выше, я реализовал новый метод Apple os_proc_available_memory()
для iOS 13+ в сочетании с NSByteCountFormatter
который предлагает ряд полезных опций форматирования для лучшего вывода памяти:
#include <os/proc.h>
....
- (NSString *)memoryStringForBytes:(unsigned long long)memoryBytes {
NSByteCountFormatter *byteFormatter = [[NSByteCountFormatter alloc] init];
byteFormatter.allowedUnits = NSByteCountFormatterUseGB;
byteFormatter.countStyle = NSByteCountFormatterCountStyleMemory;
NSString *memoryString = [byteFormatter stringFromByteCount:memoryBytes];
return memoryString;
}
- (void)memoryLoggingOutput {
if (@available(iOS 13.0, *)) {
NSLog(@"Physical memory available: %@", [self memoryStringForBytes:[NSProcessInfo processInfo].physicalMemory]);
NSLog(@"Memory A (brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory()]);
NSLog(@"Memory B (no brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory]);
}
}
Важное примечание: не забывайте()
в конце. Я включил обаNSLog
варианты в memoryLoggingOutput
потому что он не предупреждает вас об их отсутствии, а отсутствие скобок возвращает неожиданный, но постоянный результат.
Строка, возвращенная методом memoryStringForBytes
выводит такие значения:
NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.93 GB
// 2 seconds later
NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.84 GB
Речь идет не только об использовании памяти%, но и о фрагментации памяти. Если ваше приложение выделяет и освобождает большие фрагменты, смешанные с более мелкими фрагментами, возможно, эти большие фрагменты не поместятся в зарезервированную память для приложения, и ваше приложение будет "выброшено", даже если вы не использовали много своей памяти (например, на 35). % от общей суммы)
Если это так, вам следует рассмотреть возможность использования пулов памяти и повторного использования объектов. Также интенсивное использование String-Operations может плохо сказаться на вашей памяти.