Что лучше при освобождении памяти с помощью PHP: unset () или $ var = null - PullRequest
233 голосов
/ 25 февраля 2009

Я понимаю, что второе исключает накладные расходы при вызове функции ( update , на самом деле является языковой конструкцией), но было бы интересно узнать, лучше ли одно, чем другое. Я использовал unset() для большей части своего кодирования, но недавно я просмотрел несколько респектабельных классов, найденных в сети, которые вместо этого используют $var = null.

Есть ли предпочтительный вариант и в чем причина?

Ответы [ 12 ]

226 голосов
/ 25 февраля 2009

Это было упомянуто на странице unset manual in 2009 :

unset() делает только то, что говорит его имя - сбросить переменную. Это не вызывает немедленного освобождения памяти. Сборщик мусора в PHP сделает это, когда посчитает нужным - преднамеренно, так как эти циклы ЦП в любом случае не нужны, или так поздно, как сценарию не хватит памяти, что бы ни произошло первым.

Если вы делаете $whatever = null;, то вы переписываете данные переменной. Вы можете освободить / сжать память быстрее, но это может украсть циклы процессора из кода, который действительно в них нуждается, быстрее, что приведет к увеличению общего времени выполнения.

(С 2013 года unset справочная страница больше не включает этот раздел)

Обратите внимание, что до 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? Каждое выражение должно приводить к некоторому значению.)
  • Переменная с присвоенным ей нулем все еще остается совершенно нормальной переменной.
45 голосов
/ 25 февраля 2009

unset на самом деле не функция, а языковая конструкция . Это не более вызов функции, чем return или include.

Помимо проблем с производительностью, использование unset делает ваш код намерением намного понятнее.

34 голосов
/ 25 февраля 2009

Выполнив unset () для переменной, вы, по сути, пометили переменную как «сборщик мусора» (в действительности у PHP ее нет, но, например, ради), поэтому память не сразу доступна. Переменная больше не хранит данные, но стек остается большего размера. Выполнение нулевого метода удаляет данные и практически сразу сокращает объем стека.

Это было из личного опыта и других. Смотрите комментарии функции unset () здесь .

Лично я использую unset () между итерациями в цикле, так что мне не нужно, чтобы задержка стека была равна йо-йо. Данные ушли, но след остается. На следующей итерации php уже забирает память и, следовательно, быстрее инициализирует следующую переменную.

27 голосов
/ 26 ноября 2012
<?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";
?>

Похоже, что "= null" быстрее.

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.

18 голосов
/ 26 марта 2011

Это имеет значение для элементов массива.

Рассмотрим этот пример

$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";

ключ больше не существует.

17 голосов
/ 02 августа 2012

Работает по-другому для переменных, скопированных по ссылке:

$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
12 голосов
/ 20 ноября 2012

Что касается объектов, особенно в сценарии с отложенной загрузкой, следует учитывать, что сборщик мусора работает в незанятых циклах ЦП, поэтому предположим, что вы столкнетесь с проблемами, когда много объектов загружаются из-за небольшого временного штрафа. 1001 *

Используйте time_nanosleep, чтобы позволить ГХ собирать память. Желательно установить переменную в 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);
    ...
3 голосов
/ 28 ноября 2012

Я все еще сомневаюсь в этом, но я попробовал это в своем сценарии, и я использую 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 добавит процесс к вашему приложению, и будет лучше остаться в исходном коде с вашим кодом и уменьшать используемую переменную настолько эффективно, насколько вы можете.

Поправь меня, если я ошибаюсь, спасибо

2 голосов
/ 09 июня 2015

Я создал новый тест производительности для 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 для удобства чтения.

1 голос
/ 27 июня 2018

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 секунд

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...