Утечка памяти?! Правильно ли сборщик мусора, используя 'create_function' в 'array_map'?

Я нашел следующее решение здесь, в StackOverflow, чтобы получить массив определенного свойства объекта из массива объектов: PHP – Извлечение свойства из массива объектов

Предлагаемое решение – использовать array_map и внутри создать функцию с create_function следующим образом:

 $catIds = array_map(create_function('$o', 'return $o->id;'), $objects); 

Что происходит ?: array_map пробегает каждый элемент массива, в этом случае объект stdClass . Сначала он создает такую ​​функцию:

 function($o) { return $o->id; } 

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

 $catIds = array_map(function($o) { return $o->id; }, $objects); 

Но это решение работает только в PHP версии> = 5.3, поскольку использует концепцию Closure => http://php.net/manual/de/class.closure.php

Теперь настоящая проблема:

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

Таким образом, решения дают одни и те же результаты, но имеют различное поведение по отношению к памяти.

Следующий пример:

 // following array is given $objects = array ( [0] => stdClass ( [id] => 1 ), [1] => stdClass ( [id] => 2 ), [2] => stdClass ( [id] => 3 ) ) 

ПЛОХО

 while (true) { $objects = array_map(create_function('$o', 'return $o->id;'), $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235616 4236600 4237560 4238520 ... 

ХОРОШО

 while (true) { $objects = array_map(function($o) { return $o->id; }, $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235136 4235168 4235168 4235168 ... 

Я трачу так много времени, чтобы узнать это, и теперь я хочу знать, если это ошибка с сборщиком мусора или я сделал ошибку? И почему имеет смысл оставить уже созданную и вызванную функцию в памяти, когда она никогда не будет использоваться повторно?

Вот пример: http://ideone.com/9a1D5g

Обновлено : Когда я рекурсивно просматриваю свой код и его зависимости, например PEAR и Zend, я слишком часто нашел этот BAD- путь.

Обновлено : Когда две функции вложены, мы исходим изнутри, чтобы оценить это выражение. Другими словами, он сначала create_function (один раз), а возвращаемое имя функции является аргументом для одиночного вызова array_map . Но поскольку GC забывает удалить его из памяти (никакой указатель не оставлен для функции в памяти), и PHP не сможет повторно использовать функцию, уже находящуюся в памяти, позвольте мне подумать, что есть ошибка, а не только вещь с «плохой производительностью», , Эта конкретная строка кода является примером в PHPDoc и используется во многих больших средах, например Zend и PEAR, и многое другое. С одной строкой вы можете обойти эту «ошибку», проверьте. Но я не ищу решения: я ищу правду. Это ошибка, или это мой подход. И последнее я еще не мог решить.

Solutions Collecting From Web of "Утечка памяти?! Правильно ли сборщик мусора, используя 'create_function' в 'array_map'?"

В случае create_function() функция лямбда-стиля создается с помощью eval() , и возвращается строка, содержащая его имя. Затем это имя передается как аргумент функции array_map() .

Это отличается от анонимной функции типа закрытия, когда ни одна строка, содержащая имя, не используется вообще. function($o) { return $o->id; } function($o) { return $o->id; } Является функцией, или, скорее, экземпляром класса Closure.

Функция eval() внутри create_function() выполняет часть кода PHP, которая создает create_function() функцию. Примерно так:

 function create_function($arguments,$code) { $name = <_lambda_>; // just a unique string eval('function '.$name.'($arguments){$code}'); return $name; } 

Обратите внимание, что это упрощение.

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

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

 while (true) { $func = create_function('$o', 'return $o->id;'); $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); } 

Строка, содержащая ссылку (= имя) функции, была сделана expliciet и доступна здесь. Теперь, каждый раз, когда create_function() , старая функция перезаписывается новой.

Итак, нет, нет «утечки памяти», он должен работать таким образом.

Конечно, приведенный ниже код более эффективен:

 $func = create_function('$o', 'return $o->id;'); while (true) { $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); } 

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

Не используйте create_function() если вы можете избежать этого. В частности, не раз. В большом желтом окне « Предупреждение» в руководстве по PHP :

… он имеет плохую производительность и характеристики использования памяти.

Хорошо, я думаю, проблема в том, что первое решение с create_function работает на старых версиях PHP, а второе решение не увеличивает ненужную память. Но давайте посмотрим на первое решение. Метод create_function вызывается внутри array_map , а именно для каждого while повторении. Если мы хотим, чтобы решение работало со старыми версиями PHP и без увеличения объема памяти, мы должны делать следующее для более старого экземпляра функции на каждом этапе, while итерация:

 $func = create_function('$o', 'return $o->id;'); $catIds = array_map($func, $objects); 

Это все. Так просто.

Но он также не отвечает на вопрос вообще. Остается вопрос, является ли это ошибкой с PHP или функцией. Для моего понимания, что путь записи результата create_function в переменной ДОЛЖЕН быть таким же, как поставить его непосредственно в качестве параметра в array_map , не так ли?