I am trying to understand this article "PHP Manual -> Features -> Garbage Collection"
unfortunately few things are unclear for me.
1.
To avoid having to call the checking of garbage cycles with every
possible decrease of a refcount, the algorithm instead puts all
possible roots (zvals) in the "root buffer".
but what in case
<?php
$a = new \stdClass(); (1)
$a = new \stdClass();
Then I guess the first object become "lost" zval like
no_symbol : (refcount=1, is_ref=1) = stdObject
Will such "lost" zvals be added into root buffer or not? There is no handler for them.
2.
Variables created in function scope, what happened with them?
Ex:
<?php
function test($a = 'abc') {
$c = 123;
return 1;
}
test();
echo 'end';
What happened with $a and $c when gc starts?
These variables still have refcount set to 1.
Will they still be removed? if yes then why and how (what is happening under the cover?)
3.
How can it help for cyclic references?
Ex
<?php
$a = array('abc');
$a[] =& $a;
unset($a);
where
(refcount=1, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='abc',
1 => (refcount=1, is_ref=1)=...
)
1) The original object is replaced with the new one, and thus memory is freed instantly.
echo memory_get_usage().'<br/>';
$a = new stdClass();
echo memory_get_usage().'<br/>';
$a = new stdClass();
echo memory_get_usage().'<br/>';
2) They are gc'ed the second the function completes executing:
echo memory_get_usage().'<br/>';
function test($a = 'abc') {
$c = 123;
return 1;
}
echo memory_get_usage().'<br/>';
test();
echo memory_get_usage().'<br/>';
3) unsetting $a will leave the referenced variable in memory. You need to set the value to NULL first, and then unset.
echo memory_get_usage().'<br/>';
$a = array('abc');
echo memory_get_usage().'<br/>';
$a[] =& $a;
echo memory_get_usage().'<br/>';
$a = null;
unset($a);
echo memory_get_usage().'<br/>';
Related
I've been reading about memory management in PHP and learned that variables in PHP copy the reference to zvals as long as you don't do a write operation (copy on write paradigm).
https://www.phpinternalsbook.com/php5/zvals/memory_management.html#reference-counting-and-copy-on-write
However, it does not describe what happens when you reassign a value to an already copied zval.
Here's what the book says:
$a = 1; // $a = zval_1(value=1, refcount=1)
$b = $a; // $a = $b = zval_1(value=1, refcount=2)
$c = $b; // $a = $b = $c = zval_1(value=1, refcount=3)
$a++; // $b = $c = zval_1(value=1, refcount=2)
// $a = zval_2(value=2, refcount=1)
Now, if you do another $a++, will it change the value in zval,
$a++; // $a = zval_2(value=3, refcount=1)
or will it create a new zval once again?
$a++; // $a = zval_3(value=3, refcount=1)
Following the logic of the PHP Language Reference, I guess it should be more likely option #1 as long as refcount = 1 (you would not manipulate another variable).
Is there a way in PHP to determine whether a given variable is a reference to another variable and / or referenced by another variable? I appreciate that it might not be possible to separate detecting "reference to" and "reference from" given the comment on php.net that setting $a=& $b means "$a and $b are completely equal here. $a is not pointing to $b or vice versa. $a and $b are pointing to the same place."
If it's not possible to determine whether a given variable is a reference / referenced, is there a generalised way of determining if two variables are references of each other? Again, a comment on php.net supplies a function for doing such a comparison - although it is one that involves editing one of the variables and seeing if the other variable is similarly effected. I'd rather avoid doing this if possible since some of the variables I'm considering make heavy use of magic getters / setters.
The background to the request in this instance is to write a debugging function to help view structures in detail.
Full working example:
function EqualReferences(&$first, &$second){
if($first !== $second){
return false;
}
$value_of_first = $first;
$first = ($first === true) ? false : true; // modify $first
$is_ref = ($first === $second); // after modifying $first, $second will not be equal to $first, unless $second and $first points to the same variable.
$first = $value_of_first; // unmodify $first
return $is_ref;
}
$a = array('foo');
$b = array('foo');
$c = &$a;
$d = $a;
var_dump(EqualReferences($a, $b)); // false
var_dump(EqualReferences($b, $c)); // false
var_dump(EqualReferences($a, $c)); // true
var_dump(EqualReferences($a, $d)); // false
var_dump($a); // unmodified
var_dump($b); // unmodified
You can use debug_zval_dump:
function countRefs(&$var) {
ob_start();
debug_zval_dump(&$var);
preg_match('~refcount\((\d+)\)~', ob_get_clean(), $matches);
return $matches[1] - 4;
}
$var = 'A';
echo countRefs($var); // 0
$ref =& $var;
echo countRefs($var); // 1
This though will not work anymore as of PHP 5.4 as they removed call time pass by reference support and may throw an E_STRICT level error on lower versions.
If you wonder, where the -4 in the above function come from: You tell me... I got it by trying. In my eyes it should be only 3 (the variable, the variable in my function, the variable passed to zend_debug_zval), but I'm not too good at PHP internals and it seems that it creates yet another reference somewhere on the way ;)
Maybe xdebug_debug_zval() helps you. http://www.xdebug.org/docs/all_functions
Edit:
It seems I've answered the question 'is it possible to check if two variables are referencing same value in memory' not the actual question asked. :P
As far as 'plain' variables go the answer is 'no'.
As far as objects go - maybe.
All objects are by default handled by references. Also each object has it's serial number which you can see when you var_dump() it.
>> class a {};
>> $a = new a();
>> var_dump($a);
object(a)#12 (0) {
}
If you could get somehow to this #, you could effectively compare it for two variables, and see if they point to the same object. The question is how to get this number. var_export() does not return it. I don't see snything in Reflection classes that would get it either.
One thing that comes to my mind is using output buffering + regex
Take a peak at xdebug_debug_zval(). Right now, that's the only way to really know if you can determine everything about the variable's zval.
So here are a couple of helper functions to determine some helpful information:
function isRef($var) {
$info = getZvalRefCountInfo($var);
return (boolean) $info['is_ref'];
}
function getRefCount($var) {
$info = getZvalRefCountInfo($var);
return $info['refcount'];
}
function canCopyOnWrite($var) {
$info = getZvalRefCountInfo($var);
return $info['is_ref'] == 0;
}
function canReferenceWithoutCopy($var) {
$info = getZvalRefCountInfo($var);
return $info['is_ref'] == 1 || $info['refcount'] == 1;
}
function getZvalRefCountInfo($var) {
ob_start();
xdebug_debug_zval($var);
$info = ob_get_clean();
preg_match('(: \(refcount=(\d+), is_ref=(\d+)\))', $info, $match);
return array('refcount' => $match[1], 'is_ref' => $match[2]);
}
So with some sample variables:
$a = 'test';
$b = $a;
$c = $b;
$d =& $c;
$e = 'foo';
We can test if a variable is a reference:
isRef('a'); // false
isRef('c'); // true
isRef('e'); // false
We can get the number of variables linked to the zval (not necessarily a reference, can be for copy-on-write):
getRefCount('a'); // 2
getRefCount('c'); // 2
getRefCount('e'); // 1
We can test if we can copy-on-write (copy without performing a memory copy):
canCopyOnWrite('a'); // true
canCopyOnWrite('c'); // false
canCopyOnWrite('e'); // true
And we can test if we can make a reference without copying the zval:
canReferenceWithoutCopy('a'); // false
canReferenceWithoutCopy('c'); // true
canReferenceWithoutCopy('e'); // true
And now, we can check if a variable references itself through some black magic:
function isReferenceOf(&$a, &$b) {
if (!isRef('a') || getZvalRefCountInfo('a') != getZvalRefCountInfo('b')) {
return false;
}
$tmp = $a;
if (is_object($a) || is_array($a)) {
$a = 'test';
$ret = $b === 'test';
$a = $tmp;
} else {
$a = array();
$ret = $b === array();
$a = $tmp;
}
return $tmp;
}
It's a bit hacky since we can't determine what other symbols reference the same zval (only that other symbols reference). So this basically checks to see if $a is a reference, and if $a and $b both have the same refcount and reference flag set. Then, it changes one to check if the other changes (indicating they are the same reference).
Which of the following is best for memory usage?
$a = new foo();
$username = $a->getuserName('username');
$b = new bar();
$b->validateUserName($username);
//OR
( new bar() )->validateUserName( (new foo() )->getuserName($username) );
Does storing an object in a variable increase memory usage?
does PHP treats other languages differently?
Someone can explain the management of PHP memory (should and should not)
To test memory usage you can use the next template:
<?php
$mem = 0; // create variable before "memory_get_usage()";
$mem = memory_get_usage();
// Place your code here
$mem = memory_get_usage() - $mem;
print ("Memory used: $mem");
For example I created the stub:
<?php
class foo {
function getuserName($a) {
return $a;
}
}
class bar {
function validateUserName($a) {
}
}
$mem = 0; // Allocate variable before "memory_get_usage()";
$mem = memory_get_usage();
$a = new foo();
$username = $a->getuserName('username');
$b = new bar();
$b->validateUserName($username);
$mem = memory_get_usage() - $mem;
print ("Memory used: $mem\r\n");
result on php 5.6.40 win x64 is 560 bytes.
If I change the code to your second variant
...
$mem = 0; // Allocate variable before "memory_get_usage()";
$mem = memory_get_usage();
// Note I've changed $username to 'username'
(new bar())->validateUserName( (new foo())->getuserName('username') );
$mem = memory_get_usage() - $mem;
print ("Memory used: $mem\r\n");
Now result is 0 bytes.
Let's see another variant:
...
function test() {
$a = new foo();
$username = $a->getuserName('username');
$b = new bar();
$b->validateUserName($username);
}
$mem = 0; // Allocate variable before "memory_get_usage()";
$mem = memory_get_usage();
test();
$mem = memory_get_usage() - $mem;
print ("Memory used: $mem\r\n");
Memory used: 64
Let's change the function to the second variant:
...
function test() {
(new bar())->validateUserName( (new foo())->getuserName('username') );
}
...
Now result again is 64 bytes.
And one more test:
...
$mem = 0; // Allocate variable before "memory_get_usage()";
$mem = memory_get_usage();
$a = new foo();
$username = $a->getuserName('username');
$b = new bar();
$b->validateUserName($username);
unset($a);
unset($b);
unset($username);
$mem = memory_get_usage() - $mem;
print ("Memory used: $mem\r\n");
Here result is 0 again.
From there I can assume, when you're creating global variables, they remains all the time, and object they are referencing cannot be garbage collected, so they remain in the memory. As soon as you unset variables, or declare them in a particular scope (function), or do not declare them at all - there is no difference in result. To see when object are destroyed and memory is freed you can add desctructors in the classes:
...
function __destruct() {
print "foo/bar destroyed\r\n";
}
...
Still, writing calls in one single line does not allocate unnecessary variables at all, thus improving execution time.
But my recommendation: do not bother with such optimizations in a language such as php. The language itself is not that fast and not that memory effective. For example, adding each item into array may cost more than 100 bytes of memory. Sparing several bytes by making code less readable is not worth it.
In PHP How it differs when I create cloned object in a variable and new object created using a variable with the same class
For example
$a = new classA();
$b = clone $a;
$c = new classA();
What is the difference between $b and $c ?
You should look at the following example
<?php
class classA {
public $x=0;
}
$a = new classA();
$a->x = 20;
echo $a->x."<br />";
$b = clone $a;
$a->x = 30;
echo $a->x."<br />";
echo $b->x."<br />"; // 20 because x was 20 before cloning $a to $b
$a->x = 50;
echo $a->x."<br />"; // changed to 50
echo $b->x."<br />"; // stil 20, $a
$c = new classA();
echo $c->x;
Using cloning make, you have property x in object $b the same as in object $a because cloning simple copies object. And when creating new object, you will have new object and property value will be 0.
Cloning is simple copying object because by default for objects:
$a = $b;
PHP won't do copying (as for simple types) but will point to exact place in memory.
So for simple types you use:
$a = 5;
$b = $a;
if you want to make a copy, but for objects you need to use clone:
$a = new classA();
$a->x = 20;
$b = clone $a;
to have the same effect.
You should look in manual at Object and references and Cloning to understand those things.
Is there a way in PHP to determine whether a given variable is a reference to another variable and / or referenced by another variable? I appreciate that it might not be possible to separate detecting "reference to" and "reference from" given the comment on php.net that setting $a=& $b means "$a and $b are completely equal here. $a is not pointing to $b or vice versa. $a and $b are pointing to the same place."
If it's not possible to determine whether a given variable is a reference / referenced, is there a generalised way of determining if two variables are references of each other? Again, a comment on php.net supplies a function for doing such a comparison - although it is one that involves editing one of the variables and seeing if the other variable is similarly effected. I'd rather avoid doing this if possible since some of the variables I'm considering make heavy use of magic getters / setters.
The background to the request in this instance is to write a debugging function to help view structures in detail.
Full working example:
function EqualReferences(&$first, &$second){
if($first !== $second){
return false;
}
$value_of_first = $first;
$first = ($first === true) ? false : true; // modify $first
$is_ref = ($first === $second); // after modifying $first, $second will not be equal to $first, unless $second and $first points to the same variable.
$first = $value_of_first; // unmodify $first
return $is_ref;
}
$a = array('foo');
$b = array('foo');
$c = &$a;
$d = $a;
var_dump(EqualReferences($a, $b)); // false
var_dump(EqualReferences($b, $c)); // false
var_dump(EqualReferences($a, $c)); // true
var_dump(EqualReferences($a, $d)); // false
var_dump($a); // unmodified
var_dump($b); // unmodified
You can use debug_zval_dump:
function countRefs(&$var) {
ob_start();
debug_zval_dump(&$var);
preg_match('~refcount\((\d+)\)~', ob_get_clean(), $matches);
return $matches[1] - 4;
}
$var = 'A';
echo countRefs($var); // 0
$ref =& $var;
echo countRefs($var); // 1
This though will not work anymore as of PHP 5.4 as they removed call time pass by reference support and may throw an E_STRICT level error on lower versions.
If you wonder, where the -4 in the above function come from: You tell me... I got it by trying. In my eyes it should be only 3 (the variable, the variable in my function, the variable passed to zend_debug_zval), but I'm not too good at PHP internals and it seems that it creates yet another reference somewhere on the way ;)
Maybe xdebug_debug_zval() helps you. http://www.xdebug.org/docs/all_functions
Edit:
It seems I've answered the question 'is it possible to check if two variables are referencing same value in memory' not the actual question asked. :P
As far as 'plain' variables go the answer is 'no'.
As far as objects go - maybe.
All objects are by default handled by references. Also each object has it's serial number which you can see when you var_dump() it.
>> class a {};
>> $a = new a();
>> var_dump($a);
object(a)#12 (0) {
}
If you could get somehow to this #, you could effectively compare it for two variables, and see if they point to the same object. The question is how to get this number. var_export() does not return it. I don't see snything in Reflection classes that would get it either.
One thing that comes to my mind is using output buffering + regex
Take a peak at xdebug_debug_zval(). Right now, that's the only way to really know if you can determine everything about the variable's zval.
So here are a couple of helper functions to determine some helpful information:
function isRef($var) {
$info = getZvalRefCountInfo($var);
return (boolean) $info['is_ref'];
}
function getRefCount($var) {
$info = getZvalRefCountInfo($var);
return $info['refcount'];
}
function canCopyOnWrite($var) {
$info = getZvalRefCountInfo($var);
return $info['is_ref'] == 0;
}
function canReferenceWithoutCopy($var) {
$info = getZvalRefCountInfo($var);
return $info['is_ref'] == 1 || $info['refcount'] == 1;
}
function getZvalRefCountInfo($var) {
ob_start();
xdebug_debug_zval($var);
$info = ob_get_clean();
preg_match('(: \(refcount=(\d+), is_ref=(\d+)\))', $info, $match);
return array('refcount' => $match[1], 'is_ref' => $match[2]);
}
So with some sample variables:
$a = 'test';
$b = $a;
$c = $b;
$d =& $c;
$e = 'foo';
We can test if a variable is a reference:
isRef('a'); // false
isRef('c'); // true
isRef('e'); // false
We can get the number of variables linked to the zval (not necessarily a reference, can be for copy-on-write):
getRefCount('a'); // 2
getRefCount('c'); // 2
getRefCount('e'); // 1
We can test if we can copy-on-write (copy without performing a memory copy):
canCopyOnWrite('a'); // true
canCopyOnWrite('c'); // false
canCopyOnWrite('e'); // true
And we can test if we can make a reference without copying the zval:
canReferenceWithoutCopy('a'); // false
canReferenceWithoutCopy('c'); // true
canReferenceWithoutCopy('e'); // true
And now, we can check if a variable references itself through some black magic:
function isReferenceOf(&$a, &$b) {
if (!isRef('a') || getZvalRefCountInfo('a') != getZvalRefCountInfo('b')) {
return false;
}
$tmp = $a;
if (is_object($a) || is_array($a)) {
$a = 'test';
$ret = $b === 'test';
$a = $tmp;
} else {
$a = array();
$ret = $b === array();
$a = $tmp;
}
return $tmp;
}
It's a bit hacky since we can't determine what other symbols reference the same zval (only that other symbols reference). So this basically checks to see if $a is a reference, and if $a and $b both have the same refcount and reference flag set. Then, it changes one to check if the other changes (indicating they are the same reference).