Назови особенности выделения памяти для массивов в PHP.

«Назови особенности выделения памяти для массивов в PHP.» — вопрос из категории PHP Core, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В PHP массивы (тип array) — это упорядоченные хеш-таблицы, что определяет специфику работы с памятью:

  1. Динамическое выделение с запасом (capacity): При добавлении элементов PHP выделяет память не поштучно, а блоками. Если массив превышает текущую вместимость, происходит дорогостоящая операция rehash — выделение нового, большего блока памяти и перераспределение всех существующих элементов.

    $array = []; // Выделена начальная вместимость (например, на 8 элементов)
    for ($i = 0; $i < 100; $i++) {
        $array[$i] = $i; // PHP несколько раз увеличит вместимость (8 -> 16 -> 32 -> 64 -> 128)
    }
  2. Высокий overhead на элемент: Каждый элемент массива (Bucket) хранит не только значение и ключ (хэш), но и ссылки для поддержки двусвязного списка (для сохранения порядка обхода). В PHP 8.x overhead составляет около 56 байт на элемент (плюс память на само значение и ключ).

  3. Память освобождается не сразу при unset(): При удалении элемента PHP лишь помечает Bucket как неиспользуемый (IS_UNDEF). Физическое освобождение памяти и сокращение размера массива может произойти только при следующем рехеше или при вызове array_values() (который создает новый массив).

    $arr = range(1, 1000); // ~56KB + данные
    unset($arr[500]);      // Память не вернется системе, элемент помечен UNDEF
    $arr = array_values($arr); // Создается новый массив, старый удаляется GC
  4. Оптимизации:

    • SplFixedArray: Для массивов с целочисленными ключами, известными заранее, использует C-подобный массив, почти не имеющий overhead.
    • Предварительное выделение: Если известен примерный размер, можно задать его через $array = new SplFixedArray(10000); или использовать array_fill(0, $size, null) для обычного массива, чтобы минимизировать рехеши.
    • Явная очистка: Для больших массивов лучший способ освободить память — присвоить переменной null или вызвать unset() на всей переменной.