Что лучше при освобождении памяти с помощью PHP: unset() или $var = null
Я понимаю, что второй избегает накладных расходов при вызове функции (обновление, на самом деле, является языковой конструкцией), но было бы интересно узнать, лучше ли одно, чем другое. Я использую unset()
для большей части моего кода, но я недавно просмотрел несколько респектабельных классов, найденных в сети, которые используют $var = null
вместо.
Есть ли предпочтительный, и в чем причина?
12 ответов
Это было упомянуто на нерабочей странице руководства в 2009 году:
unset()
делает только то, что говорит его имя - сбросить переменную. Это не вызывает немедленного освобождения памяти. Сборщик мусора в PHP сделает это, когда посчитает нужным - преднамеренно, так как эти циклы ЦП в любом случае не нужны, или так поздно, как сценарию не хватило бы памяти, что бы ни произошло первым.Если вы делаете
$whatever = null;
тогда вы переписываете данные переменной. Вы можете освободить / сжать память быстрее, но это может украсть циклы ЦП из кода, который действительно в них нуждается, быстрее, что приведет к увеличению общего времени выполнения.
(С 2013 года, что unset
страница man больше не включает этот раздел)
Обратите внимание, что до php5.3, если у вас есть два объекта в циклической ссылке, например, в родительско-дочерних отношениях, вызов unset() для родительского объекта не освободит память, используемую для родительской ссылки в дочернем объекте. (Также память не будет освобождена, когда родительский объект будет собран мусором.) ( Ошибка 33595)
Вопрос " разница между unset и = null" детализирует некоторые различия:
unset($a)
также удаляет $a
из таблицы символов; например:
$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);
Выходы:
Notice: Undefined variable: a in xxx
NULL
Но когда
$a = null
используется:
$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:
NULL
Кажется, что
$a = null
немного быстрее, чем егоunset()
аналог: обновление записи таблицы символов происходит быстрее, чем ее удаление.
- при попытке использовать несуществующий (
unset
) переменная, ошибка будет вызвана, и значение для выражения переменной будет нулевым. (Потому что, что еще должен делать PHP? Каждое выражение должно приводить к некоторому значению.) - Переменная с присвоенным ей нулем все еще остается совершенно нормальной переменной.
unset
на самом деле это не функция, а языковая конструкция. Это не более вызов функции, чем return
или include
,
Помимо проблем с производительностью, используя unset
делает ваш код гораздо понятнее.
Выполнив unset() для переменной, вы, по сути, пометили переменную как "сборщик мусора" (в действительности у PHP такой нет, но, например, ради), поэтому память не сразу доступна. Переменная больше не хранит данные, но стек остается большего размера. Выполнение нулевого метода удаляет данные и практически сразу сокращает объем стека.
Это было из личного опыта и других. Смотрите комментарии функции unset() здесь.
Лично я использую unset() между итерациями в цикле, так что мне не нужно, чтобы задержка стека была равна размеру йо-йо. Данные ушли, но след остается. На следующей итерации php уже забирает память и, следовательно, быстрее инициализирует следующую переменную.
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
$a = NULL;
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds\r\n";
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
unset($a);
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds\r\n";
?>
Похоже, что "= ноль" быстрее.
Результаты PHP 5.4:
- заняло 0,88389301300049 секунд
- заняло 2,1757180690765 секунд
Результаты PHP 5.3:
- заняло 1.7235369682312 секунд
- заняло 2.9490959644318 секунд
Результаты PHP 5.2:
- заняло 3.0069220066071 секунд
- заняло 4.7002630233765 секунд
Результаты PHP 5.1:
- заняло 2,6272349357605 секунд
- заняло 5,0403649806976 секунд
Вещи начинают выглядеть по-другому с PHP 5.0 и 4.4.
5,0:
- заняло 10.038941144943 секунд
- заняло 7.0874409675598 секунд
4,4:
- заняло 7,5352551937103 секунд
- заняло 6,6245851516724 секунд
Имейте в виду, что microtime(true) не работает в PHP 4.4, поэтому мне пришлось использовать пример microtime_float, приведенный в php.net/microtime / Example #1.
Он работает по-другому для переменных, скопированных по ссылке:
$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5
$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null
Это имеет значение с элементами массива.
Рассмотрим этот пример
$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";
Здесь ключ 'test' все еще существует. Однако в этом примере
$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";
ключ больше не существует
Что касается объектов, особенно в сценарии с отложенной загрузкой, следует учитывать, что сборщик мусора работает в незанятых циклах ЦП, поэтому предположим, что вы столкнетесь с проблемами при загрузке большого количества объектов с небольшим временным штрафом, что приведет к освобождению памяти.
Используйте time_nanosleep, чтобы позволить GC собирать память. Установка переменной в null желательна.
Протестировано на рабочем сервере, первоначально работа потребляла 50 МБ, а затем была остановлена. После того, как был использован Nanosleep, 14 МБ потребляли постоянную память.
Следует сказать, что это зависит от поведения GC, которое может меняться от версии PHP к версии. Но это работает на PHP 5.3 нормально.
например. этот образец (код взят из VirtueMart2 в фиде Google)
for($n=0; $n<count($ids); $n++)
{
//unset($product); //usefull for arrays
$product = null
if( $n % 50 == 0 )
{
// let GC do the memory job
//echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
time_nanosleep(0, 10000000);
}
$product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
...
PHP 7 уже работал над такими проблемами управления памятью и его сокращение до минимального использования.
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
$a = NULL;
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds\r\n";
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
unset($a);
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds\r\n";
?>
PHP 7.1 Outpu:
Потребовалось 0,16778993606567 секунд. Потребовалось 0,16630101203918 секунд.
Для записи, и исключая время, которое требуется:
<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";
echo memory_get_peak_usage() . "<br>\n";
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";
echo memory_get_peak_usage() . "<br>\n";
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";
echo memory_get_peak_usage() . "<br>\n";
echo "<hr>function:<br>";
function test() {
$x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";
echo memory_get_peak_usage() . "<br>\n";
echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";
echo memory_get_peak_usage() . "<br>\n";
Возвращается
First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.
Вывод, как нулевой, так и неустановленной свободной памяти, как и ожидалось (не только в конце выполнения). Кроме того, переназначение переменной содержит значение дважды в некоторой точке (520216 против 438352)
Пример кода из комментария
echo "PHP Version: " . phpversion() . PHP_EOL . PHP_EOL;
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
$a = NULL;
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds" . PHP_EOL;
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$a = 'a';
unset($a);
}
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds" . PHP_EOL;
Запуск в контейнере докеров из образаphp:7.4-fpm
и другие..
PHP Version: 7.4.8
took 0.22569918632507 seconds null
took 0.11705803871155 seconds unset
took 0.20791196823121 seconds null
took 0.11697316169739 seconds unset
PHP Version: 7.3.20
took 0.22086310386658 seconds null
took 0.11882591247559 seconds unset
took 0.21383500099182 seconds null
took 0.11916995048523 seconds unset
PHP Version: 7.2.32
took 0.24728178977966 seconds null
took 0.12719893455505 seconds unset
took 0.23839902877808 seconds null
took 0.12744522094727 seconds unset
PHP Version: 7.1.33
took 0.51380109786987 seconds null
took 0.50135898590088 seconds unset
took 0.50358104705811 seconds null
took 0.50115609169006 seconds unset
PHP Version: 7.0.33
took 0.50918698310852 seconds null
took 0.50490307807922 seconds unset
took 0.50227618217468 seconds null
took 0.50514912605286 seconds unset
PHP Version: 5.6.40
took 1.0063569545746 seconds null
took 1.6303179264069 seconds unset
took 1.0689589977264 seconds null
took 1.6382601261139 seconds unset
PHP Version: 5.4.45
took 1.0791940689087 seconds null
took 1.6308979988098 seconds unset
took 1.0029168128967 seconds null
took 1.6320278644562 seconds unset
Но с другим примером:
<?php
ini_set("memory_limit", "512M");
echo "PHP Version: " . phpversion() . PHP_EOL . PHP_EOL;
$start = microtime(true);
$arr = [];
for ($i = 0; $i < 1000000; $i++) {
$arr[] = 'a';
}
$arr = null;
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds" . PHP_EOL;
$start = microtime(true);
$arr = [];
for ($i = 0; $i < 1000000; $i++) {
$arr[] = 'a';
}
unset($arr);
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds" . PHP_EOL;
Полученные результаты:
PHP Version: 7.4.8
took 0.053696155548096 seconds
took 0.053897857666016 seconds
PHP Version: 7.3.20
took 0.054572820663452 seconds
took 0.054342031478882 seconds
PHP Version: 7.2.32
took 0.05678391456604 seconds
took 0.057311058044434 seconds
PHP Version: 7.1.33
took 0.097366094589233 seconds
took 0.073100090026855 seconds
PHP Version: 7.0.33
took 0.076443910598755 seconds
took 0.077098846435547 seconds
PHP Version: 7.0.33
took 0.075634002685547 seconds
took 0.075317859649658 seconds
PHP Version: 5.6.40
took 0.29681086540222 seconds
took 0.28199100494385 seconds
PHP Version: 5.4.45
took 0.30513095855713 seconds
took 0.29265689849854 seconds
Я все еще сомневаюсь в этом, но я попробовал это в своем сценарии, и я использую xdebug, чтобы знать, как это повлияет на использование памяти моего приложения. Сценарий установлен на моей функции следующим образом:
function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
$sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
if($showSql === FALSE) {
$sql = mysql_query($sql) or die(mysql_error());
$data = mysql_fetch_array($sql);
return $data[0];
} else echo $sql;
}
И я добавляю unset незадолго до return
код, и это дает мне: 160200, то я пытаюсь изменить его с $sql = NULL
и это даст мне: 160224:)
Но есть что-то уникальное в этом сравнительном, когда я не использую unset() или NULL, xdebug дает мне 160144 в качестве использования памяти
Итак, я думаю, что указание строки для использования unset() или NULL добавит процесс к вашему приложению, и будет лучше оставаться исходным с вашим кодом и уменьшать используемую переменную настолько эффективно, насколько это возможно.
Поправьте меня если я не прав, спасибо
Я создал новый тест производительности для unset
а также =null
потому что, как упоминалось в комментариях, здесь написано ошибка (воссоздание элементов). Я использовал массивы, как вы видите, теперь это не имело значения.
<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
$arr1[$i] = 'a';
$arr2[$i] = 'a';
}
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
$arr1[$i] = null;
}
$elapsed = microtime(true) - $start;
echo 'took '. $elapsed .'seconds<br>';
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;
echo 'took '. $elapsed .'seconds<br>';
Но я могу только проверить это на сервере PHP 5.5.9, вот результаты: - заняло 4.4571571350098 секунд - заняло 4.4425978660583 секунд
я предпочитаю unset
для удобства чтения.
unset
код, если не освобождает немедленную память, все еще очень полезен и будет хорошей практикой делать это каждый раз, когда мы передаем шаги кода перед выходом из метода. обратите внимание, это не об освобождении немедленной памяти. Непосредственная память для процессора, а как насчет вторичной памяти, которая является ОЗУ.
и это также решает проблему предотвращения утечек памяти.
пожалуйста, смотрите эту ссылку http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2
Я давно использую unset.
Лучше практиковать подобное в коде, чтобы мгновенно сбрасывать все переменные, которые уже использовались в качестве массива.
$data['tesst']='';
$data['test2']='asdadsa';
....
nth.
а также just unset($data);
освободить все переменные использования.
пожалуйста, смотрите связанную тему, чтобы сбросить
Насколько важно сбросить переменные в PHP?
[Ошибка]