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

219

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

Есть ли предпочтительный вариант, и что такое рассуждение?

Теги:

11 ответов

203
Лучший ответ

На странице unset manual было указано:

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

Если вы выполняете $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? Каждое выражение должно приводить к некоторому значению.)
  • Переменная с присвоенным ей значением по-прежнему является совершенно нормальной переменной.
  • 17
    Обратите внимание, что если $whatever указывает на объект, $whatever = null перезаписывает указатель, а не сам объект, поэтому он действует в основном так же, как unset() .
  • 1
    @VonC: неустановленная цитата на php.net, на которую вы ссылаетесь, больше не существует.
Показать ещё 12 комментариев
44

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

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

  • 0
    Вот почему я всегда использовал их, лично я думал, что они выглядят лучше, чем $ var = null. Кстати, я всегда использовал NULL полные заглавные буквы ... но теперь я не знаю почему?
  • 0
    @alex: может быть потому что это константа? См. Drupal.org/node/217379
Показать ещё 6 комментариев
30

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

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

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

  • 15
    Установка чего-либо в NULL может быть полезной, если память, необходимая для хранения значения NULL, меньше, чем та, которая требуется для хранения любого значения, которое было ранее. Например, длинная строка. Если строка не была константой и ее счетчик ссылок падает до нуля, то эта память должна быть освобождена. Unset чище - он больше не поддерживает ссылку. Вам нужно подождать сбора мусора, но можно с уверенностью считать, что он не занимает память, так как из-за нехватки памяти будет запускаться сборка мусора.
  • 0
    мы не можем использовать оба? равно нулю, а затем не установлен?
Показать ещё 1 комментарий
24
<?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.

  • 6
    Я думаю, что ваш тест ошибочен. Первый цикл - это простое переназначение, а второй цикл уничтожает и воссоздает один и тот же символ. Если тест переделан с unset массивом, это происходит быстрее. У меня есть тест, который позже проверяет существование в unset случае. В этом тесте установка null немного быстрее. Тест: pastebin.com/fUe57C51
  • 4
    @ansur, всегда вызывайте gc_collect_cycles перед запуском таймера, чтобы получить более точные результаты.
Показать ещё 2 комментария
18

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

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

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

Здесь ключевой "тест" все еще существует. Однако в этом примере

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

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

15

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

$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
  • 5
    Я кодирую php уже несколько лет и никогда не видел "&" по поводу ссылки на оригинальный var. Спасибо +1 :)
  • 1
    $ А = 78; $ Ь = $ а; снята с охраны ($ а); var_dump ($ б); // 78; var_dump ($ a); // Неопределенная переменная: a
10

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

Используйте time_nanosleep, чтобы включить GC для сбора памяти. Требуется установка переменной в нуль.

Протестировано на производственном сервере, изначально работа была потрачена на 50 МБ, а затем была остановлена. После использования нанослопа 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

Я все еще сомневаюсь в этом, но я пробовал его на своем script, и я использую xdebug, чтобы узнать, как это повлияет на использование моей памяти приложения. script устанавливается на мою функцию следующим образом:

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

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

  • 0
    Я думаю, что пока вы возвращаете элемент $ data [0], на весь массив ссылаются / но это всего лишь гипотеза. Попробуйте скопировать $ data [0] в локальную переменную, установить массив в null и вернуть локальную переменную. Хорошая предыстория здесь tuxradar.com/practicalphp/18/1/11 и ofc. php.net/manual/en/features.gc.php
2

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

unset код, если не освобождение немедленной памяти, по-прежнему очень полезен и будет хорошей практикой делать это каждый раз, когда мы передаем шаги кода перед тем, как выйти из метода. обратите внимание на то, что это не освобождение памяти. непосредственная память для CPU, а о вторичной памяти - ОЗУ.

и это также касается предотвращения утечек памяти.

посмотрите эту ссылку http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

Я использую unset в течение длительного времени.

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

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

и just unset($data);, чтобы освободить все использование переменных.

см. соответствующую тему для unset

Насколько важно отключать переменные в PHP?

[ошибка]

0

Для записи и исключения времени, которое требуется:

<?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)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню