What is reference counting

In PHP’s data structures, reference counting is the name of each variable. In addition to its type and value, it also stores two additional things: whether the variable is currently referenced and how many times it was referenced. Why save two more? For garbage collection (GC), of course. That is, when the number of references reaches zero, the variable is no longer used and can be reclaimed through GC to free up memory resources. No program can occupy unlimited memory resources all the time, too large memory footprint often leads to a serious problem, that is, memory leaks, and GC is PHP low-level automatic memory destruction for us, like C must be manually free.

How do I check reference counts?

We need to install the Xdebug extension and use the xdebug_debug_zval() function to see details about the specified memory, such as:

$a = "I am a String";
xdebug_debug_zval('a');
// a: (refcount=1, is_ref=0)='I am a String'
Copy the code

As you can see from the above, the content of the $a variable is a String such as I am a String. The refcount in parentheses is the number of references, and is_ref indicates whether the variable is referenced. Let’s use variable assignment to see how these two parameters change.

$b = $a;
xdebug_debug_zval('a');
// a: (refcount=1, is_ref=0)='I am a String'

$b = &$a;
xdebug_debug_zval('a');
// a: (refcount=2, is_ref=1)='I am a String'
Copy the code

When we do the normal assignment, refcount and is_ref do not change at all, but when we do the reference assignment, we can see that refcount becomes 2 and is_ref becomes 1. This means that the current \ A variable is referenced and assigned, and its memory symbol table serves both a and $b.

$c = &$a;
xdebug_debug_zval('a');
// a: (refcount=3, is_ref=1)='I am a String'

unset($c.$b);
xdebug_debug_zval('a');
// a: (refcount=1, is_ref=1)='I am a String'

$b = &$a;
$c = &$a;
$b = "I am a String new";
xdebug_debug_zval('a');
// a: (refcount=3, is_ref=1)='I am a String new'

unset($a);
xdebug_debug_zval('a');
// a: no such symbol
Copy the code

Continue to add a reference assignment to C, and you can see that the refcount continues to increase. Then unset the reference assignment from C, and you can see that the refcount continues to increase. Then unset the reference assignment from C, and you can see that the refcount continues to increase. Refcount = refref = 1, refref = 1, refref = 1, refref = 1, refref = 1

Unset $a to indicate no such symbol. The current variable has been destroyed and is no longer a usable symbol reference. (Note that variables in PHP correspond to the symbol table of memory, not the actual memory address.)

Object reference count

Object variables use the same counting rules as variables of ordinary types.

// Object reference count
class A{}$objA = new A();
xdebug_debug_zval('objA');
// objA: (refcount=1, is_ref=0)=class A { }

$objB = $objA;
xdebug_debug_zval('objA');
// objA: (refcount=2, is_ref=0)=class A { }

$objC = $objA;
xdebug_debug_zval('objA');
// objA: (refcount=3, is_ref=0)=class A { }

unset($objB);
class C{}$objC = new C;
xdebug_debug_zval('objA');
// objA: (refcount=1, is_ref=0)=class A { }
Copy the code

But it’s important to note that the object’s symbol table is a set of connections, which means that re-instantiating or changing objC to NULL does not affect the re-instantiation or changing objC to NULL. It doesn’t affect the content of objA, the knowledge that we have in the previous object assignment is reference in PHP at all? This has been explained in the article. Object is also a symbol table assignment of reference type, so we don’t need an ampersand.

Array reference count

// Array reference count
$arrA = [
    'a'= >1.'b'= >2,]; xdebug_debug_zval('arrA');
// arrA: (refcount=2, is_ref=0)=array (
// 'a' => (refcount=0, is_ref=0)=1,
// 'b' => (refcount=0, is_ref=0)=2
// )

$arrB = $arrA;
$arrC = $arrA;
xdebug_debug_zval('arrA');
// arrA: (refcount=4, is_ref=0)=array (
// 'a' => (refcount=0, is_ref=0)=1,
// 'b' => (refcount=0, is_ref=0)=2
// )

unset($arrB);
$arrC = ['c'= >3];
xdebug_debug_zval('arrA');
// arrA: (refcount=2, is_ref=0)=array (
// 'a' => (refcount=0, is_ref=0)=1,
// 'b' => (refcount=0, is_ref=0)=2
// )

// Add an existing element
$arrA['c'] = &$arrA['a'];
xdebug_debug_zval('arrA');
// arrA: (refcount=1, is_ref=0)=array (
// 'a' => (refcount=2, is_ref=1)=1,
// 'b' => (refcount=0, is_ref=0)=2,
// 'c' => (refcount=2, is_ref=1)=1
// )
Copy the code

Two interesting things happen when we debug arrays.

One is that each element inside the array has its own separate reference count. And this makes sense, because each element of an array can be viewed as an individual variable, but an array is just a hash set of variables. The same applies if there are member variables in the object. When an element in an array is assigned by an & reference to another variable, the element’s refcount increases without affecting the entire array’s refcount.

The default refcount is 2. This is a new feature in PHP7 that, when an array is defined and initialized, turns it into an immutable array. To distinguish it from a normal array, the refcount starts at 2. When we modify any element in this array, the array will go back to the normal array, so the refCount will go back to 1. This we can try, on why to do so, the official explanation is for efficiency, the specific principle may still need to dig PHP7 source code to know.

What to watch out for about memory leaks

PHP already does GC for you, so you don’t need to worry too much about destroying and releasing variables. However, it is important to note that elements in an object or array can be assigned to themselves. In other words, assigning a reference to an element becomes a circular reference. This object is less likely to be destroyed by GC.

// Object loop reference
class D{
    public $d;
}
$d = new D;
$d->d = $d;
xdebug_debug_zval('d');
// d: (refcount=2, is_ref=0)=class D { 
// public $d = (refcount=2, is_ref=0)=...
// }

// Array loop reference
$arrA['arrA'] = &$arrA;
xdebug_debug_zval('arrA');
// arrA: (refcount=2, is_ref=1)=array (
// 'a' => (refcount=0, is_ref=0)=1,
// 'b' => (refcount=0, is_ref=0)=2,
// 'arrA' => (refcount=2, is_ref=1)=...
// )
Copy the code

Both objects and arrays appear during print debugging… Ellipsis, then you have circular references in your program. We talked about circular references in our previous article on object copying in PHP, so it’s something we should keep an eye on in our daily development.

conclusion

Reference counting is a prerequisite for understanding garbage collection, and it’s the fact that modern languages have a similar garbage collection mechanism that makes programming easier and safer. So someone said, everyday development doesn’t need this at all? Just because you don’t need it doesn’t mean you shouldn’t learn it. Just like the problem of circular reference, when your code is filled with a lot of similar code, it’s only a matter of time before the system crashes, so this knowledge is essential for you to move up the program ladder.

Test code: github.com/zhangyue050…

Reference documents: www.php.net/manual/zh/f… Ask.csdn.net/questions/7… www.jianshu.com/p/52450a613…