Как можно распределить память с умеренным размером в 64-разрядном процессе на Mac OS X?

Я создаю приложение макета фотокниги. Приложение часто распаковывает изображения JPEG в буферы растровых изображений в памяти. Размер изображений ограничен 100 мегапикселями (в то время как они обычно не превышают 15 мегапикселей).

Иногда выделяются выделения памяти для этих буферов: [[NSMutableData alloc] initWithLength:] возвращает nil. Это, похоже, происходит в ситуациях, когда системная свободная физическая память приближается к нулю.

Мое понимание системы виртуальной памяти в Mac OS X заключалось в том, что выделение в 64-битном процессе практически (sic) не может потерпеть неудачу, Есть 16 экзабайт адресного пространства, из которых я пытаюсь выделить максимум 400 мегабайт за раз. Теоретически я мог выделить 40 миллиардов этих буферов, не нажимая жесткого предела доступного адресного пространства. Разумеется, практические ограничения будут препятствовать этому сценарию, поскольку пространство подкачки ограничено размером загрузочного тома. На самом деле я делаю очень мало таких распределений (менее десяти).

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

Приложение - сбор мусора.

Edit:

У меня было время немного углубиться в эту проблему, и вот мои выводы:

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

Я предполагаю, что NSData использует NSAllocateCollectable для выполнения выделения вместо malloc при запуске под сборкой мусора.

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

Ответ 1

Ответ заключается в реализации libauto.

Начиная с OS X 10.6 арена 8 Гб выделяется для сбора собранных мусора на 64-битных платформах. Эта арена разрезается пополам для больших распределений ( >= 128k) и небольших (< 2048b) или средних (< 128k) распределений.

Таким образом, по сути, на 10.6 у вас есть 4 ГБ памяти, доступной для больших распределений собранной памяти мусора. На 10.5 арене имело размер 32Gb, но Apple уменьшила этот размер до 8Gb на 10,6.

Ответ 2

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

ulimit -a

В консоль. Для меня я получаю:

~ iainmcgin$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 256
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 266
virtual memory          (kbytes, -v) unlimited

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

Я использую Snow Leopard:

~ iainmcgin$ uname -rs
Darwin 10.6.0

Ответ 3

Несмотря на то, что 64-битный компьютер теоретически может адресовать 18 EB, текущие процессоры ограничены 256 ТБ. Конечно, вы тоже не достигли этого предела. Но объем памяти, которую может использовать ваш процесс за один раз, ограничен объемом доступной оперативной памяти. ОС также может ограничить объем оперативной памяти, которую вы можете использовать. Согласно опубликованной вами ссылке: "Даже для компьютеров с 4 или более гигабайтами оперативной памяти система редко выделяет эту много RAM для одного процесса".

Ответ 4

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

Ответ 5

Это может быть проблема фрагментации памяти. Возможно, в момент выделения не существует ни одного непрерывного фрагмента в 400 МБ?

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

Ответ 6

initWithBytes:length: пытается выделить всю свою длину в активной памяти, по существу эквивалентную malloc() этого размера. Если длина превышает доступную память, вы получите нуль. Если вы хотите использовать большие файлы с NSData, я бы рекомендовал initWithContentsOfMappedFile: или аналогичные инициализаторы, поскольку они используют систему VM для извлечения частей файла в и из активной памяти, когда это необходимо.