Что такое Copy-on-Write (COW) в PHP?

Ответ

Copy-on-Write (COW) — это внутренняя оптимизация в PHP, которая откладывает фактическое копирование данных (например, массива или строки) до момента, когда одна из переменных пытается изменить эти данные. Пока данные только читаются, несколько переменных могут ссылаться на одну и ту же область памяти, что экономит ресурсы.

Наглядный пример с массивом:

// Исходный массив занимает память X
$originalArray = [1, 2, 3, 4, 5];

// Присваивание. Копирования НЕТ.
// $copyArray и $originalArray ссылаются на одни и те же данные (zval).
$copyArray = $originalArray;

// Пока мы только читаем, память не дублируется.
echo $copyArray[0]; // Выводит: 1

// МОМЕНТ КОПИРОВАНИЯ (Write).
// Теперь, когда мы пытаемся изменить $copyArray,
// PHP создает физическую копию данных для $copyArray.
$copyArray[] = 6;

// Теперь $originalArray и $copyArray — это два независимых массива в памяти.
print_r($originalArray); // [1, 2, 3, 4, 5]
print_r($copyArray);     // [1, 2, 3, 4, 5, 6]

Где это применяется и почему важно:

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

Важное отличие для объектов: Начиная с PHP 5, объекты по умолчанию передаются и присваиваются по ссылке на объект (object handle). Механизм COW к самим объектам не применяется, но может применяться к их внутренним свойствам-массивам.

Ответ 18+ 🔞

Давай разберём эту штуку, а то звучит как какая-то магия, а на деле всё просто, как три копейки.

Вот представь: у тебя есть здоровенный массив, ну, овердохуища данных. И тебе нужно его в другую переменную скопировать. Если бы PHP был тупым, как пробка, он бы тупо скопировал все данные, и память бы улетела в два раза. Ебать колотить, какой же он был бы тогда распиздяй.

Но он не такой. Он хитрожопый. Он использует Copy-on-Write (COW), что дословно значит «копирование при записи». Суть в чём? Пока ты данные только читаешь — копирования нихуя не происходит. Все переменные просто тихо, как мыши, смотрят на одну и ту же кучку данных в памяти. А вот в тот самый момент, когда какая-то из них возомнит себя главной и попытается эти данные изменить — вот тут-то PHP и говорит: «Ага, чувак, ты хочешь писать? Получай свою личную, отдельную копию, чтобы другим не мешать». И только тогда происходит настоящее копирование.

Смотри на примере, тут всё как на ладони:

// Создаём массив. Память занята под него.
$originalArray = [1, 2, 3, 4, 5];

// Присваиваем другой переменной. ВНИМАНИЕ!
// Копирования НЕТ. Вообще. Ноль.
// $copyArray и $originalArray — это как два парня, которые смотрят на одну и ту же бутылку водки.
$copyArray = $originalArray;

// Читаем. Всё ок, водку пока не трогаем, пьём из одной.
echo $copyArray[0]; // Выводит: 1

// А ВОТ ТЕПЕРЬ ПИЗДЕЦ (в хорошем смысле). МОМЕНТ ИСТИНЫ.
// Мы пытаемся ИЗМЕНИТЬ $copyArray. Добавить новый элемент.
// PHP резко просыпается: «Так, стоп, **ёпта**! Этот чувак хочет в эту водку свой огурец кинуть! Давайте ему его отдельную бутылку!»
// И ТОЛЬКО СЕЙЧАС происходит реальное копирование данных в памяти.
$copyArray[] = 6;

// Теперь у каждого своя буханочка. Независимые массивы.
print_r($originalArray); // [1, 2, 3, 4, 5] — оригинал нетронут, огурец не плавает.
print_r($copyArray);     // [1, 2, 3, 4, 5, 6] — а тут уже полный ажур.

Зачем этот цирк? Да всё просто, я тебя умоляю.

  1. Экономия памяти, просто пиздец какая. Особенно когда у тебя мегабайтные массивы и ты их в десять функций передаёшь. Без COW память бы кончилась быстрее, чем терпение у тестировщика в пятницу вечером.
  2. Передача в функцию. Передал массив в функцию — и не парься, что он скопируется. Скопируется только если функция начнёт в нём ковыряться.
  3. Возврат из функции. Вернул массив — он не скопируется, пока ты его не модифицируешь.

НО! Важный нюанс, про который все забывают, а потом охуевают. С объектами в PHP (классами) — история другая. Начиная с пятой версии, объекты по умолчанию передаются и присваиваются по ссылке на объект (типа по хэндлу). Сам механизм COW к целому объекту не применяется. То есть, если ты скопируешь объект в другую переменную и поменяешь свойство — оно поменяется и в оригинале, потому что это один и тот же объект. Но! Если внутри объекта есть свойство-массив, то вот к этому внутреннему массиву COW уже может примениться. Вот такая, блядь, матрёшка получается.

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