При поиске чего-то еще в заголовочных файлах и документации я недавно обнаружил следующий прототип функции в <objc/objc-auto.h>
:
void *objc_memmove_collectable(void *dst, const void *src, size_t size);
Он находится в середине группы функций после комментария, который говорит: «Запишите барьеры. Используется компилятором». Эти функции используются для информирования сборщика мусора о том, что память, которой он управляет, была изменена, чтобы он мог сканировать ссылки, а не случайно восстанавливать память, которая должна быть укоренена сильной ссылкой, о которой он не знает. Я знаю, что такое правильное использование __strong
почти всегда заставит компилятор вставить правильную функцию, но я также слышал, что инженеры Apple говорят, что бывают крайне редкие случаи, когда вы должны вызывать такие функции напрямую. (Я считаю это когда файл не скомпилирован как Objective-C, такой как код C, который записывает в память, управляемую GC, но я не уверен.)
В документе Apple Objective-C Runtime для Mac OS X v10.5 указано, что об этой функции говорится следующее (последний абзац под первым заголовком)
"При массовом копировании памяти в блок сбора мусора необходимо использовать API objc_memmove_collectable(void *dst, const void *src, size_t size)
."
Эта функция, по-видимому, предназначена для перемещения элементов из не-GC-памяти в GC-память, и архивы дискуссий по электронной почте, похоже, предполагают, что цель состоит в том, чтобы вызвать только один барьер записи для большой блочной копии. (Например, 1000 отдельных записей с барьером записи для каждой, по сравнению с 1 массовой копией с одним барьером записи для всей области памяти, в которую выполняется запись.) Это один случай, когда необходимо использовать , но Документы ничего не говорят о том, когда не следует (или не нужно) использовать.
Например, у меня есть блок памяти, который я выделил с NSAllocateCollectable()
и NSScannedOption
, и я использую его как динамически расширяющийся циклический буфер. Если буфер заполнится, я удваиваю его размер, используя NSReallocateCollectable()
и NSScannedOption
. Часть, которая оборачивается (между первым слотом в массиве и последним объектом в буфере), копируется / перемещается в начало второй половины массива. Затем я bzero()
слотов, откуда были скопированы данные, чтобы избежать чрезмерного укоренения перемещенных объектов. (См. Строки 460-467 в этого файла . Да, код работает как есть - он полностью тестируется модулем, и я не видел сбоев с тех пор, как некоторое время назад добавил атрибут __strong
.)
Вопросы:
- Когда необходимо использовать
objc_memmove_collectable()
вместо memmove()
или memcpy()
?
- Например, что, если источником и назначением являются память, управляемая GC? (Моя память объявлена как
__strong id *array;
, поэтому я предполагаю, что компилятор вставляет барьер.)
- Если это не нужно, поможет ли это улучшить производительность ГХ? (Например, поддерживает ли он какую-либо блокировку или помогает GC избежать сканирования вручную?) Считается ли это хорошим / плохим стилем?
Редактировать: Поскольку memcpy
и memmove
вообще не взаимодействуют с GC, мне было интересно, почему я не видел сбоев в памяти, собираемой из-под меня. Мое лучшее предположение на данный момент состоит в том, что, поскольку bzero
также ничего не сообщает GC, сборщик не узнает об обнуленной памяти и перемещенных данных, пока в следующий раз не просканирует весь этот блок памяти. Если сборщик все еще считает обнуленные ссылки как корни и еще не считает новые области памяти, это объясняет, почему значения не собираются преждевременно. Это звучит правильно?