This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What's better at freeing memory with PHP: unset() or $var = null
As far as the garbage collection goes, is 1 better than the other in any circumstances?
EDIT:
Particularly if $var is a very large variable with a lot of levels of recursion and other objects (so to do with recursive cleanup of large objects)
EDIT:
Removed this:
I can think of only 1 thing, and that's that isset($var) will respond differently in either case.
Because apparently I was mistaken... They both react the same.
unset($var);
// You will get Undefined variable Notice.
if ($var) {}
$var = null;
// it's ok.
if ($var) {}
Addition of GC of php.
PHP's Garbage Collection is based on the refcount of the zval, if refcount becomes 0, then the zval can be freed.
So if $a = $b = array(/*a very large array*/);, unset only $a or only $b won't free the memory of the large array.
unset($a); or $a = null or assign another value to $a will all let the refcount decrease by 1, but the memory will be freed only when the refcount decrease to 0.
unset does not force immediate memory freeing but leaves it for the gc. $var = null; however forces immediate memory release.
See example:
// $a = NULL; (better I think)
$a = 5;
$b = & $a;
$a = NULL;
print "b $b "; // b
print(! isset($b)); // 1
?>
It is also worthy to note that in the case of an array unset destroys the variable completely. i.e.:
<?php
$ar = array(1,2,3,4);
var_dump($ar);
echo "<br />";
unset($ar[2]);
var_dump($ar);
echo "<br />";
$ar[1] = null;
var_dump($ar);
?>
Returns the output:
array(4) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) }
array(3) { [0]=> int(1) [1]=> int(2) [3]=> int(4) }
array(3) { [0]=> int(1) [1]=> NULL [3]=> int(4) }
$a = 5;
$b = &$a;
unset($b); //just say $b should not pointer to any var
print $a; // 5
$a = 5;
$b = &$a;
$b = null;
print $a; // nothing, because $a = null
Related
I have following code
public function index() {
$a = 2;
$b = 8;
$arr[] = $a;
$arr[] = &$a;
$arr[] = $a;
$this->dmp($arr);
$arr[1] = $b;
$this->dmp($arr);
$a++;
$b++;
$this->dmp($arr);
}
private function dmp($val) {
echo '<pre>';
var_dump($val);
echo '</pre>';
}
which gives me following result
array(3) {
[0]=>
int(2)
[1]=>
&int(2)
[2]=>
int(2)
}
array(3) {
[0]=>
int(2)
[1]=>
&int(8)
[2]=>
int(2)
}
array(3) {
[0]=>
int(2)
[1]=>
&int(9)
[2]=>
int(2)
}
Q: Why the value on index 1 is not replaced with value of $b variable, but there is still the reference? The reference remains even after calling $arr[1] = $b; without the appersand?
Here is a simple explanation:
$arr[0] = $a; // Index 0 holds the same value as $a which is 2
$arr[1] = &$a; // Index 1 holds a reference to variable $a
$arr[2] = $a; // Index 2 holds the same value as $a which is 2
You then instruct the code to change the value of the variable referenced by index 1, to hold the same value as $b which is 8.
$arr[1] = $b; // Index 1 references variable $a so $a will become 8
If you do var_dump($a) now, you will see that $a is 8.
You then increment both $a and $b, which at this point have the same value. They both will now have the value 9
The contents of the array at the end are the same as initially specified. The only difference is that the variable which is referenced by element with index 1 has now a different value.
$arr[0] = 2; // Index 0 holds the value 2
$arr[1] = &$a; // Index 1 holds a reference to variable $a which has a value 9 now
$arr[2] = 2; // Index 2 holds the value 2
When you assign by reference it creates a link between the two, they are pointing to the same result. So when you make another assignment it also assigns to the referenced variable. If you inspect $a you will see it changes to 8 and then 9. To break the reference use unset().
Demo
This $arr[] = &$a; says make $arr[1] point to the same data as $a.
This $arr[1] = $b; says assign the value of $b to $arr[1], which is 8, and $a is linked to $arr[1] so $a is 8 as well.
Check What References Do
Whats the meaning of $arr[0] result "2" in the code given below, $arr2 is copying $arr and increasing its first value by one, so the result of $arr2[0] "2" is understaning,but whats happening with $arr, when i pass by reference $arr[0] to $a like so $a=&$arr[0] the result of $arr[0] is 2, when i pass it by value $a=$arr[0[ the result of $arr[0] would be set to 1 as it should, can anyone enlighten me on this?
<?php
$a = 1;
$arr = array(1);
$a = &$arr[0];
$arr2 = $arr;
$arr2[0]++;
echo $arr[0]. "<br>";//2
echo $arr2[0]. "<br>";//2
?>
References in PHP are not like pointers; when you assign or pass something by reference, you create what we might call a "reference set" where both variables are references to the same "zval" (the structure in memory which holds the type and value of a variable).
An important consequence of this is that references are symmetrical. I tend to write assign by reference as $foo =& $bar rather than $foo = &$bar to emphasise this: the operator doesn't just take a reference to $bar and put it into $foo, it affects both operands.
With that in mind, let's go through your example:
$arr = array(1);
This will create two zvals: one for the array $arr itself, and one for the value in position 0 of that array ($arr[0]).
$a =& $arr[0];
This binds $a and $arr[0] into a reference set. Whichever of those names we use, we will be using the same zval that was previously created, and currently holds 1.
$arr2 = $arr;
This copies the contents of $arr into a new array zval, $arr2. But it doesn't resolve the references inside that array into values, it just copies the fact that they are a reference.
So $arr2[0] is now also part of the reference set containing $arr[0] and $a.
$arr2[0]++;
This increments the zval pointed to by our reference set, from 1 to 2.
echo $arr[0]. "<br>";//2
echo $arr2[0]. "<br>";//2
Since these are both part of the same reference set, they both point to the same zval, which is integer 2.
The inevitable question is, "bug or feature?" A more useful example would be passing an array containing references into a function:
function foo($array) {
$array[0] = 42;
$array[1] = 69;
}
$a = 1;
$b = 1;
$foo = [ &$a, &$b ];
foo($foo);
echo $a; // 42
echo $b; // 69
Like assignment, passing into the function didn't break the references, so code manipulating them can safely be re-factored into multiple functions.
Not sure if this is a bug but var_dump() can help to explain.
<?php
$a = 1;
$arr = array(1);
var_dump( $arr );
$a = &$arr[0];
var_dump( $arr );
$arr2 = $arr;
$arr2[0]++;
Output:
array(1) {
[0]=>
int(1)
}
array(1) {
[0]=>
&int(1)
}
Take note of &int(1) in the second var_dump(). This tells us that position #0 of $arr has been turned into a reference pointer to a position in PHP's memory instead of remaining a dedicated value.
So when you perform $arr2 = $arr;, $arr2 receives that reference pointer as well.
I found that a common var $g can't be changed in a function; but object $e can be. Why?
<?php
class e {
public $var1 = 1;
}
function f($e,$g) {
$e->var1 = 2;
$f = 2;
}
$e = new e;
$g = 1;
var_dump($e->var1);
var_dump($g);
f($e,$g);
var_dump($e->var1);
var_dump($g);
result:
int(1)
int(1)
int(2)
int(1)
Objects are passed and assigned by reference.
So when you change the 'parameter' variable, you're also changing it's original value.
This does not happen with other parameter types (strings, ints, arrays), but only with objects.
For more details, read References and Objects
Also, I belive there's a typo in your f($e,$g): shouldn't it be $g = 2?
If you need to tamper with the object but still keep a copy it's original value, do a clone:
$b = clone $var;
becase:
$b = $var;
will result in the same thing. $b will still point to $var and all changes on $b will reflect on $var;
I have this php code
$a = array('one');
$b[0] = &$a[0];
$c = $a;
$b[0] = 'three';
$a = array('two');
var_dump($a); echo '<br/>';
var_dump($b); echo '<br/>';
var_dump($c); echo '<br/>';
Which outputs this ->
array(1) { [0]=> string(3) "two" }
array(1) { [0]=> &string(5) "three" }
array(1) { [0]=> &string(5) "three" }
$b and $a points to the same value but $c should not point to the same value, it should have its own copy of the value. Then when I change the value of $b to three I don't understand why the value of $c also changes. How can I prevent this? Also, when I change the value of $a to 'two', why doesn't $b also changes to 'two'?
Thanks in advance.
Your problem is:
$b[0] = &$a[0];
This references the first element of the array. As evident by the var_dump() output &string(5).
So $b does not reference the entire array, only the first element.
To reference the entire array, you need to do:
$b = &$a;
Doing so works as you expect:
// $a
array(1) {
[0]=>
string(3) "two"
}
// $b
array(1) {
[0]=>
string(3) "two"
}
// $c
array(1) {
[0]=>
string(3) "one"
}
Read more about Arrays in PHP.
This is a step-by-step explanation:
$a = array('one');
$b[0] = &$a[0];
$b is now an array and its first element references the first element of $a. In the symbol table, $b[0] and $a[0] point to the same underlying zval.
$c = $a;
$c references $a now, but a copy has not been made (copy on write semantics). It looks a lot like this:
$c ----/----> $a $b
(0: 'one') 0: 'one' <---- 0: 'one'
The next statement:
$b[0] = 'three';
This updates $a[0] as well, and in turn updates $c[0], because $a itself is not changed. It now looks like;
$c ----/----> $a $b
(0: 'three') 0: 'three' <---- 0: 'three'
Next statement:
$a = array('two');
Disconnect $a from $c and $a[0] from $b[0].
$c $a $b
0: 'three' 0: 'two' 0: 'three'
Prevention
To prevent this behaviour, you need to reference the whole array instead of just one element:
$b = &$a;
By doing this, $b and $a now reference the same zval (the array itself), so any changes made will disconnect it from $c.
I would consider these edge cases of the language though and I would advice not to use references if you don't need to.
It's important to understand what you're doing when assigning by reference vs value. Let's go line by line.
$a = array('one');
Memory location created, we'll call it M1. $a points to M1 and within M1 we have an array with 1 entry, let's call it M1-A1.
$b[0] = &$a[0];
Now what we're doing is pointing $b[0] to M1-A1. Remember that $a[0] also points to M1-A1, so both are pointing to this particular part of memory. Remember that $b itself has it's own memory location, M2, but within M2 we point to M1-A1 (i.e. M2-A1 is pointing to M1-A1).
$c = $a;
Since we're not assigning by reference, what we get is a new memory location, let's call it M3, but within M3 there is an array with the first entry still pointing to M1-A1.
So now we have M1, M2, M3 with an array entry in M2 and M3 pointing to M1-A1.
$b[0] = 'three';
Since $b[0] actually points to M1-A1, we're actually changing the value of M1-A1 memory spot. So anything that references M1-A1's spot will also see this value change.
$a = array('two');
We're completely changing the memory location for $a at this point. Originally it was M1, now we're creating a new memory location, M4. Nothing else points to M4 and M4-A1 DOES NOT point to M1-A1.
So when we do the var dump we get the values you mentioned.
I probably made this more confusing but try drawing it on paper and it'll be pretty clear. Understand that everything is stored in memory and variables just point to memory locations. Once you understand that principle, it'll all fall in place.
They all refer to the same array object in memory
I'm working on a tree structure in PHP. When looping through the nodes, an exception is sometime thrown because some node that should not be null is null, more precisely it's set to "&NULL":
array(13) {
// ...
// some data...
// ...
["segments"]=>
NULL
["leaf"]=>
bool(false)
["children"]=>
&NULL
}
Since it's not inside quotes, I assume it's some kind of special value, but what does it mean?
It just means that it is a reference to a value NULL
$a = array();
$n = null;
$a[1] =& $n;
var_dump($a); // array(1) { [1]=> &NULL }
If you change $n = null; to $n = 1; - then you'll get &int(1)
It's a reference to a value that is null. "&" is the reference symbol.