Подсказки для выделения памяти

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

Следующие разделы описывают основные опции для выделения памяти вместе с подсказками для того, чтобы сделать так эффективно. Чтобы определить, имеет ли Ваше приложение проблемы производительности памяти во-первых, необходимо использовать инструменты Xcode для рассмотрения образцов выделения приложения, в то время как оно работает. Для получения информации о том, как сделать это, посмотрите Использование памяти Отслеживания.

Подсказки для улучшения связанной с памятью производительности

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

Задержите свои выделения памяти

Каждому выделению памяти стоили производительности. Та стоимость включает время, которое требуется для выделения памяти в логическом адресном пространстве программы и время, которое требуется для присвоения того адресного пространства физической памяти. Если Вы не планируете использовать определенный блок памяти сразу же, задерживая выделение, пока время при фактической необходимости в нем не является лучшим планом действий. Например, для предотвращения появления приложения, запускающегося медленно, минимизируйте объем памяти, который Вы выделяете во время запуска. Вместо этого фокусируйтесь, Ваши начальные выделения памяти на объектах должны были вывести на экран Ваш пользовательский интерфейс и реагировать на ввод от пользователя. Задержите другие выделения, пока пользователь не выходит, начинает взаимодействовать с Вашим приложением и давать команды. Это ленивое выделение памяти экономит время сразу же и гарантирует, что фактически используется любая выделяющаяся память.

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

Перечисление 1  Ленивое выделение памяти через средство доступа

MyGlobalInfo* GetGlobalBuffer()
{
    static MyGlobalInfo* sGlobalBuffer = NULL;
    if ( sGlobalBuffer == NULL )
        {
            sGlobalBuffer = malloc( sizeof( MyGlobalInfo ) );
        }
        return sGlobalBuffer;
}

Единственное время необходимо быть осторожными с кодом этого вида, - когда это можно было бы вызвать от многократных потоков. В многопоточной среде необходимо использовать блокировки для защиты if оператор в Вашем методе доступа. Недостаток к тому подходу, хотя то, что получение блокировки занимает нетривиальное количество времени и должно быть сделано каждый раз, когда Вы получаете доступ к глобальной переменной, которая является хитом производительности различного вида. Более простой подход должен был бы инициализировать все глобальные переменные от основного потока Вашего приложения, прежде чем это породит любые дополнительные потоки.

Инициализируйте блоки памяти эффективно

Маленькие блоки памяти, выделенное использование malloc функционируйте, как гарантируют, не будут инициализированы с, обнуляет. Несмотря на то, что Вы могли использовать memset функция для инициализации памяти лучший выбор состоит в том, чтобы использовать calloc подпрограмма для выделения памяти во-первых. calloc функционируйте резервирует требуемое виртуальное адресное пространство для памяти, но ожидает, пока память фактически не используется прежде, чем инициализировать его. Этот подход намного более эффективен, чем использование memset, который вынуждает систему виртуальной памяти отобразить соответствующие страницы в физическую память для обнуления - инициализируют их. Другое преимущество использования calloc функция состоит в том, что это позволяет системе инициализировать страницы, поскольку они используются, в противоположность одновременно.

Буферы временной памяти повторного использования

Если у Вас есть высоко используемая функция, создающая большой временный буфер для некоторых вычислений, Вы могли бы хотеть рассмотреть многократное использование, которые буферизуют вместо того, чтобы перераспределить его каждый раз, когда Вы вызываете функцию. Даже если для Вашей функции нужно переменное пространство буфера, можно всегда увеличивать буфер по мере необходимости с помощью realloc функция. Для многопоточных приложений лучший способ снова использовать буферы состоит в том, чтобы добавить их к Вашей локальной памяти потока. Несмотря на то, что Вы могли сохранить буфер с помощью статической переменной в функции, делание так будет препятствовать тому, чтобы Вы использовали ту функцию на многократных потоках одновременно.

Кэширование буферов устраняет большую часть издержек для функций, регулярно выделяющих и свободные большие блоки памяти. Однако этот метод является только подходящим для функций, часто вызывающихся. Кроме того, необходимо бояться кэш слишком много больших буферов. Кэширование буферов действительно добавляет к объему потребляемой памяти Вашего приложения и должно только использоваться, если тестирование указывает, что привело бы к лучшей производительности.

Свободная неиспользованная память

Для памяти, выделенной, пользуясь malloc библиотекой, важно высвободить память, как только Вы сделаны с помощью него. Упущение высвободить память может вызвать утечки памяти, который сокращает объем памяти, доступный Вашему приложению, и влияет на производительность. Оставленный неконтролируемый, утечки памяти могут также поместить Ваше приложение в состояние, где оно ничего не может сделать, потому что оно не может выделить требуемую память.

Независимо от того, который платформа Вы предназначаетесь, необходимо всегда устранять утечки памяти в приложении. Для кода, использующего malloc, помните, что быть ленивым хорошо для выделения памяти, но не будьте ленивы об освобождении той памяти. Чтобы помочь разыскать утечки памяти в Ваших приложениях, используйте Инструментальное приложение.

Методы выделения памяти

Поскольку память является таким фундаментальным ресурсом, OS X и iOS, оба обеспечивают несколько способов выделить его. Какие методы выделения, которые Вы используете, будут зависеть главным образом от Ваших потребностей, но в конце все выделения памяти в конечном счете пользуются malloc библиотекой для создания памяти. Даже объекты Какао выделяются, пользуясь malloc библиотекой в конечном счете. Использование этой единственной библиотеки позволяет инструментам производительности сообщить относительно всех выделений памяти в Вашем приложении.

Если Вы пишете приложение Какао, Вы могли бы выделить память только в форме объектов с помощью alloc метод NSObject. Несмотря на это, когда необходимо пойти вне основных связанных с объектом блоков памяти и использовать другие методы выделения памяти, могут быть времена. Например, Вы могли бы выделить память непосредственно с помощью malloc для передачи его низкоуровневому вызову функции.

Следующие разделы предоставляют информацию о malloc библиотеке и системе виртуальной памяти и как они выполняют выделения. Цель этих разделов состоит в том, чтобы помочь Вам идентифицировать затраты, связанные с каждым типом специализированного выделения. Необходимо использовать эту информацию для оптимизации выделений памяти в коде.

Выделение объектов

Поскольку Objective C базировал приложения, Вы выделяете объекты с помощью одного из двух методов. Можно или использовать alloc метод класса, сопровождаемый вызовом к методу инициализации класса, или можно использовать new метод класса выделить объект и вызвать его значение по умолчанию init метод за один шаг.

После создания объекта функция ARC компилятора определяет продолжительность жизни объекта и когда это должно быть удалено. Каждому новому объекту нужна по крайней мере одна сильная ссылка к нему, чтобы препятствовать тому, чтобы он был освобожден сразу же. Поэтому при создании нового объекта необходимо всегда создавать по крайней мере одну сильную ссылку к нему. После этого можно создать дополнительные сильные или слабые ссылки в зависимости от потребностей кода. Когда все сильные ссылки к объекту удалены, компилятор автоматически освобождает его.

Для получения дополнительной информации о ARC и как Вы управляете продолжительностью жизни объектов, посмотрите Переход к Информации о версии ARC.

Выделение небольших блоков памяти Используя Malloc

Для маленьких выделений памяти, где маленький что-то меньшее чем несколько страниц виртуальной памяти, malloc распределяет требуемый объем памяти из списка (или «пул») свободных блоков увеличивающегося размера. Любые маленькие блоки Вы освобождаете использование free подпрограмма добавляется назад к пулу и снова используется на «наиболее подходящей» основе. Сам пул памяти состоит из нескольких страниц виртуальной памяти, выделяющихся с помощью vm_allocate подпрограмма и управляемый для Вас системой.

При выделении любых маленьких блоков памяти помните, что гранулярность для блоков, выделенных malloc библиотекой, составляет 16 байтов. Таким образом самый маленький блок памяти, которую можно выделить, составляет 16 байтов, и любые блоки, больше, чем это, являются кратным числом 16. Например, если Вы вызываете malloc и попросите 4 байта, это возвращает блок, размер которого составляет 16 байтов; если Вы запрашиваете 24 байта, это возвращает блок, размер которого составляет 32 байта. Из-за этой гранулярности необходимо разработать структуры данных тщательно и попытаться сделать их сетью магазинов 16 байтов, когда это возможно.

Выделение Блочного использования Памяти большой емкости Malloc

Для выделений памяти большой емкости, где большой что-то большее чем несколько страниц виртуальной памяти, malloc автоматически использование vm_allocate подпрограмма для получения требуемой памяти. vm_allocate подпрограмма присваивает диапазон адресов новому блоку в логическом адресном пространстве текущего процесса, но это не присваивает физической памяти тем страницам сразу же. Вместо этого ядро делает следующее:

  1. Это отображает диапазон памяти в виртуальном адресном пространстве этого процесса путем создания записи карты; запись карты является простой структурой, определяющей начальные и конечные адреса области.

  2. Диапазон памяти поддерживается пейджером по умолчанию.

  3. Ядро создает и инициализирует объект VM, связывая его с записью карты.

В этой точке нет никакого резидентного объекта страниц в физической памяти и никаких страниц в запоминающем устройстве. Все отображается фактически в системе. Когда Ваша часть доступов кода блока памяти, путем чтения или записи в определенный адрес в нем, отказ происходит, потому что тот адрес не был отображен на физической памяти. В OS X ядро также распознает, что объект VM не имеет никакого запоминающего устройства для страницы, на которой происходит этот адрес. Ядро тогда выполняет следующие шаги для каждого отсутствия страницы:

  1. Это получает страницу из бесплатного списка и заполняется, это с обнуляет.

  2. Это вставляет ссылку на эту страницу в списке объекта VM резидентных страниц.

  3. Это отображается, виртуальная страница к физической странице путем заполнения структуры данных вызвала pmap. pmap содержит таблицу страниц, используемую процессором (или отдельным блоком управления памятью) для отображения данного виртуального адреса на фактический аппаратный адрес.

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

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

Перечисление 2  , Выделяющее память с vm_allocate

void* AllocateVirtualMemory(size_t size)
{
    char*          data;
    kern_return_t   err;
 
    // In debug builds, check that we have
    // correct VM page alignment
    check(size != 0);
    check((size % 4096) == 0);
 
    // Allocate directly from VM
    err = vm_allocate(  (vm_map_t) mach_task_self(),
                        (vm_address_t*) &data,
                        size,
                        VM_FLAGS_ANYWHERE);
 
    // Check errors
    check(err == KERN_SUCCESS);
    if(err != KERN_SUCCESS)
    {
        data = NULL;
    }
 
    return data;
}

Выделение памяти в пакетах

Если Ваш код выделяет многократные, тождественно измеренные блоки памяти, можно использовать malloc_zone_batch_malloc функция для выделения тех блоков одновременно. Эта функция предлагает лучшую производительность, чем серия вызовов к malloc выделить ту же память. Когда отдельный размер блока является относительно маленьким — меньше, чем 4K в размере, производительность является лучшей. Функция прилагает все усилия для выделения всей требуемой памяти, но может возвратить меньше, чем требовалось. При использовании этой функции проверьте возвращаемые значения тщательно для наблюдения, сколько блоков было фактически выделено.

Пакетное выделение блоков памяти поддерживается в версии 10.3 OS X и позже и в iOS. Для получения информации посмотрите /usr/include/malloc/malloc.h заголовочный файл.

Выделение общей памяти

Общая память является памятью, которая может быть записана в или считана из двумя или больше процессами. Общая память может быть наследована от родительского процесса, создаваемого сервером общей памяти, или явно создаваемого приложением для экспорта в другие приложения. Использование для общей памяти включает следующее:

  • Совместное использование больших ресурсов, таких как значки или звуки

  • Быстрая коммуникация между одним или более процессами

Когда другой, более надежные альтернативы доступны, общая память хрупка и обычно не рекомендуется. Если одна программа повреждает раздел общей памяти, любые программы, также использующие ту память, совместно используют поврежденные данные. Функции раньше создавали и управляли, области общей памяти находятся в /usr/include/sys/shm.h заголовочный файл.

Используя зоны памяти Malloc

Все блоки памяти выделяются в malloc зоне (также называемый malloc «кучей»). Зона является диапазоном переменного размера виртуальной памяти, от которой система памяти может выделить блоки. Зона имеет свой собственный бесплатный список и пул страниц памяти, и память, выделенная в зоне, остается на том наборе страниц. Зоны полезны в ситуациях, где необходимо создать блоки памяти с подобными схемами доступа или временами жизни. Можно выделить много объектов или блоков памяти в зоне и затем уничтожить зону, чтобы освободить их всех, вместо того, чтобы выпустить каждый блок индивидуально. В теории, с помощью зоны таким образом может минимизировать потраченное впустую пространство и сократить действие разбивки на страницы. В действительности издержки зон часто устраняют преимущества производительности, связанные с зоной.

По умолчанию выделения сделали использование malloc функция происходит в значении по умолчанию malloc зона, создающаяся когда malloc сначала вызывается Вашим приложением. Несмотря на то, что это обычно не рекомендуется, можно создать дополнительные зоны, если измерения показывают там, чтобы быть потенциальным увеличением производительности в коде. Например, если эффект выпуска большого количества временных (и изолированный) объекты замедляют Ваше приложение, Вы могли выделить их в зоне вместо этого и просто освободить зону.

Если Вы, создают объекты (или выделите блоки памяти) в пользовательской malloc зоне можно просто освободить всю зону, когда Вы сделаны с нею, вместо того, чтобы выпустить выделенные зоне объекты или блоки памяти индивидуально. При выполнении так, быть уверенными структуры данных приложения не содержат ссылки на память в пользовательской зоне. Попытка получить доступ к памяти в освобожденной зоне вызовет отказ памяти и разрушит Ваше приложение.

На malloc уровне библиотеки поддержка зон определяется в /usr/include/malloc/malloc.h. Используйте malloc_create_zone функция для создания пользовательской malloc зоны или malloc_default_zone функция для получения зоны по умолчанию для приложения. Для выделения памяти в определенной зоне используйте malloc_zone_malloc , malloc_zone_calloc , malloc_zone_valloc , или malloc_zone_realloc функции. Для выпуска памяти в пользовательской зоне вызвать malloc_destroy_zone.

Копирование памяти Используя Malloc

Существует два основных подхода к копированию памяти в OS X: прямой и задержанный. Для большинства ситуаций прямой подход предлагает лучшую общую производительность. Однако существуют времена при использовании работы задержанной копии, обладает ее преимуществами. Когда Вы могли бы использовать те подходы, цель следующих разделов состоит в том, чтобы представить Вас разным подходам для копирования памяти и ситуаций.

Копирование памяти непосредственно

Прямое копирование памяти включает использование подпрограммы такой как memcpy или memmove скопировать байты от одного блока до другого. И источник и целевые блоки должны быть резидентным объектом в памяти во время копии. Однако эти подпрограммы особенно подходят для следующих ситуаций:

  • Размер блока, который Вы хотите скопировать, является маленьким (менее чем 16 килобайтов).

  • Вы намереваетесь использовать или источник или место назначения сразу же.

  • Источник или целевой блок не являются выровненной страницей.

  • Источник и место назначения блокируют перекрытие.

Если Вы не планируете использовать источник, или целевые данные в течение некоторого времени, выполняя прямую копию могут уменьшить производительность значительно для блоков памяти большой емкости. Копирование памяти непосредственно увеличивает размер рабочего набора Вашего приложения. Каждый раз, когда Вы увеличиваете рабочий набор своего приложения, Вы увеличиваете возможности разбивки на страницы к диску. Если у Вас есть две прямых копии блока памяти большой емкости в Вашем рабочем наборе, Вы могли бы закончить разбивка на страницы их обоих к диску. Когда Вы более поздний доступ или источник или место назначения, необходимо было бы тогда загрузить те данные назад из диска, который является намного более дорогим, чем использование vm_copy выполнять задержанную работу копии.

Задержка операций копирования в памяти

Если Вы намереваетесь скопировать много ценности страниц памяти, но не намереваетесь использовать или источник или целевые страницы сразу, то можно хотеть использовать vm_copy функция, чтобы сделать так. В отличие от этого memmove или memcpy, vm_copy не касается никакой реальной памяти. Это изменяет карту виртуальной памяти, чтобы указать, что диапазон адреса назначения является версией копии на записи диапазона исходного адреса.

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

Копирование мелких сумм данных

Если необходимо скопировать маленькие блоки неперекрывающихся данных, необходимо предпочесть memcpy по любым другим подпрограммам. Для маленьких блоков памяти компилятор GCC может оптимизировать эту подпрограмму путем замены его встроенными инструкциями для копирования данных значением. Компилятор может не оптимизировать другие подпрограммы такой как memmove или BlockMoveData.

Копирование данных к видеопамяти

При копировании данных в VRAM используйте BlockMoveDataUncachedфункция вместо функций такой как bcopy. bcopy функционируйте инструкции манипулирования кэша использованием, которые могут вызвать ошибки исключения. Ядро должно фиксировать эти ошибки для продолжения, который замедляет производительность чрезвычайно.

Ответ на Предупреждения Низкой Памяти в iOS

Система виртуальной памяти в iOS не использует запоминающее устройство и вместо этого полагается на сотрудничество приложений для удаления сильных ссылок к объектам. Когда число свободных падений страниц ниже вычисленного порога, система выпускает неизмененные страницы каждый раз, когда возможный, но может также послать в настоящее время запущенное заявление уведомление низкой памяти. Если Ваше приложение получает это уведомление, учтите предупреждение. После получения его Ваше приложение должно удалить сильные ссылки к как можно большему количеству объектов. Например, можно использовать предупреждения убрать кэши данных, которые могут быть воссозданы позже.

UIKit обеспечивает несколько способов получить уведомления низкой памяти, включая следующее:

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

Если у Вас есть только несколько пользовательских объектов с известными purgeable ресурсами, у Вас могут быть те объекты регистр для UIApplicationDidReceiveMemoryWarningNotification уведомление и удаляет ссылки туда. Если Вы имеете много purgeable объектов или хотите выборочно произвести чистку только некоторых объектов, однако, Вы могли бы хотеть использовать своего делегата приложения для решения который объекты сохранить.