265

I realise the second one avoids the overhead of a function call (update, is actually a language construct), but it would be interesting to know if one is better than the other. I have been using unset() for most of my coding, but I've recently looked through a few respectable classes found off the net that use $var = null instead.

Is there a preferred one, and what is the reasoning?

alex
  • 479,566
  • 201
  • 878
  • 984

13 Answers13

254

It was mentioned in the unset manual's page in 2009:

unset() does just what its name says - unset a variable. It does not force immediate memory freeing. PHP's garbage collector will do it when it see fits - by intention as soon, as those CPU cycles aren't needed anyway, or as late as before the script would run out of memory, whatever occurs first.

If you are doing $whatever = null; then you are rewriting variable's data. You might get memory freed / shrunk faster, but it may steal CPU cycles from the code that truly needs them sooner, resulting in a longer overall execution time.

(Since 2013, that unset man page don't include that section anymore)

Note that until php5.3, if you have two objects in circular reference, such as in a parent-child relationship, calling unset() on the parent object will not free the memory used for the parent reference in the child object. (Nor will the memory be freed when the parent object is garbage-collected.) (bug 33595)


The question "difference between unset and = null" details some differences:


unset($a) also removes $a from the symbol table; for example:

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

Outputs:

Notice: Undefined variable: a in xxx
NULL

But when $a = null is used:

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);

Outputs:

NULL

It seems that $a = null is a bit faster than its unset() counterpart: updating a symbol table entry appears to be faster than removing it.


  • when you try to use a non-existent (unset) variable, an error will be triggered and the value for the variable expression will be null. (Because, what else should PHP do? Every expression needs to result in some value.)
  • A variable with null assigned to it is still a perfectly normal variable though.
Al Foиce ѫ
  • 4,195
  • 12
  • 39
  • 49
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 20
    Note that if `$whatever` points to an object, `$whatever = null` overwrites the pointer, not the object itself, so it acts basically the same as `unset()`. – Gras Double Mar 11 '13 at 20:23
  • 2
    @VonC: the unset quote on php.net you're referring to doesn't exist anymore. – Jürgen Thelen Nov 29 '13 at 12:08
  • @JürgenThelen true, but the content of that old answer still seems relevant, no? – VonC Nov 29 '13 at 12:14
  • 1
    @VonC: Definitely. I'm just not sure about "CPU cycles aren't needed" and "before .. out of memory" triggers garbage collection. See http://stackoverflow.com/q/20230626/693207. Maybe you can shed some light? – Jürgen Thelen Nov 29 '13 at 12:19
  • @JürgenThelen isn't what http://stackoverflow.com/q/13667137/6309 tries to explain? – VonC Nov 29 '13 at 12:26
  • @VonC: misunderstanding, sorry. I just wanted to inform you, that this quote is gone. I realized this while searching on another issue (where to find the C code triggering garbage collection "when it sees fit"?). For me it's not about unset or assigning null is better, but to prove that such C code really exists. Shouldn't have mixed this up, sorry. – Jürgen Thelen Nov 29 '13 at 12:59
  • @JürgenThelen ok. Thank you for the heads up. I already have edited the answer to make that evolution more apparent. – VonC Nov 29 '13 at 12:59
  • @VonC I have taken some of your comments to explain this in my blog. – Sanjay Kumar N S Jul 18 '14 at 05:59
  • @vonc Could you please edit the first paragraph?! `As (it was in 2009) mentioned in unset (now 2013, that man page don't include that anymore)`. It is difficult to understand and has possible misspelling errors, especially at `that man page don't include that` – Omar Dec 08 '16 at 17:39
  • @Omar I will in a bit (I am commuting from work) You can edit the answer too, and I will approve your edit. – VonC Dec 08 '16 at 17:44
  • @vonc I'll wait for you to do it because I have no idea what that means and what you try to say – Omar Dec 08 '16 at 17:54
  • 1
    @Omar I have edited the answer: The unset man page from 2009 (I have linked to the 2009 version) does include a section which is no longer present in the current version of that same page. – VonC Dec 08 '16 at 20:48
54

unset is not actually a function, but a language construct. It is no more a function call than a return or an include.

Aside from performance issues, using unset makes your code's intent much clearer.

Alex Barrett
  • 16,175
  • 3
  • 52
  • 51
  • That's why I always used them, personally I thought they looked better than $var = null. By the way, I've always used NULL full caps... but now I don't know why ? – alex Feb 25 '09 at 11:48
  • 1
    @VonC: Yeah, I figured that, but why can you use lowercase true, false and null? – alex Feb 25 '09 at 22:55
  • I also made the assumption language constructs didn't use parenthesis, like echo, require, include etc aern't echo(), require() or include(). – alex Feb 25 '09 at 22:56
  • 3
    @alex, you can sort of do that with unset. For example "$test = 4; (unset) $test;" - strange but true, and it returns the value of $test before unsetting it. Regardless, the PHP manual does confirm that it is a language construct. – thomasrutter May 15 '09 at 07:22
  • 5
    @alex: PSR-2 [requires](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md#25-keywords-and-truefalsenull) lowercase for all keywords. – Tgr Nov 23 '12 at 09:57
  • 2
    @alex - PHP keywords are case-insensitive; you could also spell `unset` as `UnSeT`, for instance. The community has settled on all-lowercase as a matter of style, but other casings still work. – Mark Reed May 01 '13 at 16:41
  • 1
    @Tgr, PHP is PHP. PSR is PSR. – Pacerier Aug 07 '13 at 21:12
40

By doing an unset() on a variable, you've essentially marked the variable for 'garbage collection' (PHP doesn't really have one, but for example's sake) so the memory isn't immediately available. The variable no longer houses the data, but the stack remains at the larger size. Doing the null method drops the data and shrinks the stack memory almost immediately.

This has been from personal experience and others as well. See the comments of the unset() function here.

I personally use unset() between iterations in a loop so that I don't have to have the delay of the stack being yo-yo'd in size. The data is gone, but the footprint remains. On the next iteration, the memory is already being taken by php and thus, quicker to initialize the next variable.

William Holroyd
  • 3,344
  • 1
  • 21
  • 25
  • 17
    Setting something to NULL can be of benefit if the memory required to hold the value NULL is less than that required to hold whatever value it was previously holding. For example, a long string. If the string wasn't a constant and its reference count drops to zero, then that memory should be freed. Unset is cleaner - it no longer maintains a reference. You do have to wait for garbage collection, but it's safe to treat it as occupying no memory, because a low memory condition will trigger garbage collection. – thomasrutter May 15 '09 at 07:29
  • 1
    can't we use both? equal to null and then unset? – Nabeel Khan Jan 13 '16 at 02:10
  • 3
    @NabeelKhan I would suggest using unset() inside loops and then nullify it when you exit the loop. Otherwise, there is a performance impact to doing both inside the loop. If you're not using loops, then just nullify as it already does the unset() logic behind the scenes. – William Holroyd Feb 12 '16 at 19:33
29
<?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";
?>

Per that it seems like "= null" is faster.

PHP 5.4 results:

  • took 0.88389301300049 seconds
  • took 2.1757180690765 seconds

PHP 5.3 results:

  • took 1.7235369682312 seconds
  • took 2.9490959644318 seconds

PHP 5.2 results:

  • took 3.0069220066071 seconds
  • took 4.7002630233765 seconds

PHP 5.1 results:

  • took 2.6272349357605 seconds
  • took 5.0403649806976 seconds

Things start to look different with PHP 5.0 and 4.4.

5.0:

  • took 10.038941144943 seconds
  • took 7.0874409675598 seconds

4.4:

  • took 7.5352551937103 seconds
  • took 6.6245851516724 seconds

Keep in mind microtime(true) doesn't work in PHP 4.4 so I had to use the microtime_float example given in php.net/microtime / Example #1.

  • 7
    I think your test is flawed. The first loop is simple reassignment and the second loop destroys and recreates the same symbol. If the test is redone with an array `unset` is faster. I have a test which later checks for existence in the `unset` case. In that test setting it to `null` is marginally faster. Test: http://pastebin.com/fUe57C51 – Knyri Jul 19 '13 at 14:12
  • 5
    @ansur, always call `gc_collect_cycles` before starting the timer to get more accurate results. – Pacerier Aug 07 '13 at 21:16
  • @knyri can you please link to that? – Nabeel Khan Jan 13 '16 at 02:12
  • @NabeelKhan I no longer have the results of that test; but there is a link to the test code in my previous comment. – Knyri Jan 13 '16 at 16:46
  • With PHP 8 I get the following results: took 0.13596701622009 seconds, took 0.12726712226868 seconds. So unset is marginally faster. – hunter Jul 22 '21 at 10:37
21

It works in a different way for variables copied by reference:

$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
Antti29
  • 2,953
  • 12
  • 34
  • 36
RiaD
  • 46,822
  • 11
  • 79
  • 123
  • 7
    I have been coding php a few years now and never seen "&" about referencing the original var. Thanks + 1 :) – Chris Sep 24 '12 at 23:56
  • 1
    $a=78; $b=$a; unset($a); var_dump($b);//78; var_dump($a);//Undefined variable: a – zloctb Sep 27 '13 at 15:58
19

It makes a difference with array elements.

Consider this example

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

Here, the key 'test' still exists. However, in this example

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

the key no longer exists.

auris
  • 701
  • 6
  • 5
15

Regarding objects, especially in lazy-load scenario, one should consider garbage collector is running in idle CPU cycles, so presuming you're going into trouble when a lot of objects are loading small time penalty will solve the memory freeing.

Use time_nanosleep to enable GC to collect memory. Setting variable to null is desirable.

Tested on production server, originally the job consumed 50MB and then was halted. After nanosleep was used 14MB was constant memory consumption.

One should say this depends on GC behaviour which may change from PHP version to version. But it works on PHP 5.3 fine.

eg. this sample (code taken form VirtueMart2 google feed)

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);
    ...
OSP
  • 1,458
  • 1
  • 14
  • 15
4

For the record, and excluding the time that it takes:

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

It returns

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

Conclusion, both null and unset free memory as expected (not only at the end of the execution). Also, reassigning a variable holds the value twice at some point (520216 versus 438352)

magallanes
  • 6,583
  • 4
  • 54
  • 55
4

PHP 7 is already worked on such memory management issues and its reduced up-to minimal usage.

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

took 0.16778993606567 seconds took 0.16630101203918 seconds

Swapnil
  • 91
  • 3
4

Code example from comment

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;

Running in docker container from image php:7.4-fpm and others..

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

But, with other example:

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

Results:

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

3

I still doubt about this, but I've tried it at my script and I'm using xdebug to know how it will affect my app memory usage. The script is set on my function like this :

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;
}

And I add unset just before the return code and it give me : 160200 then I try to change it with $sql = NULL and it give me : 160224 :)

But there is something unique on this comparative when I am not using unset() or NULL, xdebug give me 160144 as memory usage

So, I think giving line to use unset() or NULL will add process to your application and it will be better to stay origin with your code and decrease the variable that you are using as effective as you can .

Correct me if I'm wrong, thanks

Anggie Aziz
  • 133
  • 1
  • 1
  • 10
  • I think while you're returning $data[0] item, the whole array is referenced / but it's just the hypothesis. Try to copy $data[0] to local variable, set the array to null and return the local variable. Good background is here http://www.tuxradar.com/practicalphp/18/1/11 and ofc. http://php.net/manual/en/features.gc.php – OSP Apr 02 '13 at 05:25
2

I created a new performance test for unset and =null, because as mentioned in the comments the here written has an error (the recreating of the elements). I used arrays, as you see it didn't matter now.

<?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>';

But i can only test it on an PHP 5.5.9 server, here the results: - took 4.4571571350098 seconds - took 4.4425978660583 seconds

I prefer unset for readability reasons.

Michael B.
  • 899
  • 10
  • 16
1

unset code if not freeing immediate memory is still very helpful and would be a good practice to do this each time we pass on code steps before we exit a method. take note its not about freeing immediate memory. immediate memory is for CPU, what about secondary memory which is RAM.

and this also tackles about preventing memory leaks.

please see this link http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

i have been using unset for a long time now.

better practice like this in code to instanly unset all variable that have been used already as array.

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

and just unset($data); to free all variable usage.

please see related topic to unset

How important is it to unset variables in PHP?

[bug]

Community
  • 1
  • 1
zero8
  • 1,997
  • 3
  • 15
  • 25