The PHP garbage collection mechanism is familiar but not familiar to PHPer. So how does PHP recycle unwanted memory?
The internal storage structure of PHP variables
First of all, we need to understand the basics, so that we can understand the principles of garbage collection. We all know that PHP is written in C, so the internal storage structure of PHP variables is also related to C, namely the structure of zval:
struct _zval_struct {
union {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} value; // Value specifies the value of the variable
zend_uint refcount__gc; // Reference counts the number of times the variable is used in memory
zend_uchar type; // Variable type
zend_uchar is_ref__gc; // Distinguish whether it is a reference variable
};
Copy the code
As can be seen from the above structure, each PHP variable is composed of four parts: variable type, value value, number of reference counts, and whether it is a reference variable
Note: the zval structure above is built after php5.3. Before php5.3, there was no new garbage collection mechanism called GC, so there is no _gc. After the PHP7 version due to performance problems so rewritten zval structure, no longer expressed here
Principle of reference counting
After looking at the internal storage structure of PHP variables, let’s take a look at the principles of PHP variable assignment and the early garbage collection mechanisms
Variable container
Non-array and object variables
Each time a constant is assigned to a variable, a variable container is generated
For example:
$a = 'The Path of Xu Zheng's Technological Growth';
xdebug_debug_zval('a')
Copy the code
Results:
a: (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'
Copy the code
Array and object variables
Produces a variable container with +1 elements
For example:
$b = [
'name'= >'The Path of Xu Zheng's Technological Growth'.'number'= >3
];
xdebug_debug_zval('b')
Copy the code
Results:
b: (refcount=1, is_ref=0) =array ('name' => (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'.'number' => (refcount=1, is_ref=0) =3)
Copy the code
Assignment principle (copy-on-write technique)
Now that we know about constant assignments, let’s think about assignments between variables in terms of memory
For example:
$a = [
'name'= >'The Path of Xu Zheng's Technological Growth'.'number'= >3
]; // Create a variable container. The variable a points to the variable container and a's ref_count is 1
$b = $a; // Variable B also points to the variable container that variable A points to. Ref_count of a and B is 2
xdebug_debug_zval('a'.'b');
$b['name'] = 'Xu Zheng's Technological Development 1';// If one of the elements of variable B changes, a new variable container is copied, variable B refers to the new variable container again, and the ref_count of a and B becomes 1
xdebug_debug_zval('a'.'b');
Copy the code
Results:
a: (refcount=2, is_ref=0) =array ('name' => (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'.'number' => (refcount=1, is_ref=0) =3)
b: (refcount=2, is_ref=0) =array ('name' => (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'.'number' => (refcount=1, is_ref=0) =3)
a: (refcount=1, is_ref=0) =array ('name' => (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'.'number' => (refcount=1, is_ref=0) =3)
b: (refcount=1, is_ref=0) =array ('name' => (refcount=1, is_ref=0) ='Xu Zheng's Technological Development 1'.'number' => (refcount=1, is_ref=0) =3)
Copy the code
So, when variable A is assigned to variable B, it does not immediately create a new variable container. Instead, variable B points to the variable container that variable A points to, i.e. the memory “share”. When one of the elements of variable B changes, the variable container copy actually occurs. This is the copy-on-write technique
The reference count is clear at 0
When the ref_count of a variable container is zero, the variable container is destroyed, implementing garbage collection, which was the previous garbage collection mechanism in PHP5.3
For example:
$a = "The Path of Xu Zheng's Technological Growth";
$b = $a;
xdebug_debug_zval('a');
unset($b);
xdebug_debug_zval('a');
Copy the code
Results:
a: (refcount=2, is_ref=0) ='The Path of Xu Zheng's Technological Growth'
a: (refcount=1, is_ref=0) ='The Path of Xu Zheng's Technological Growth'
Copy the code
Memory leak problems caused by circular references
Garbage collection but php5.3 version before there is a loophole, internal child elements as an array or object reference element of his father, and if there is a delete elements of his father, the variable container will not be deleted, because its elements are pointing to the variable container, but because they did not point to all within the scope of the variable container symbols, It cannot be cleaned up, so a memory leak occurs until the script is finished executing
For example:
$a = array( 'one' );
$a[] = &$a;
xdebug_debug_zval( 'a' );
Copy the code
Since this example does not output well, it is represented by a graph, as shown below:
For example:
unset($a);
xdebug_debug_zval('a');
Copy the code
As shown in figure:
New garbage collection mechanism
PHP starts with a root buffer with a specified number of zVal’s by default (10000 by default). When PHP finds a zVal with a circular reference, it puts it into the root buffer. When the root buffer reaches the specified number in the configuration file (10000 by default), the root buffer will be used as the root buffer. Garbage collection is done to resolve memory leaks caused by circular references
Criteria for identifying as garbage
If the reference count of a zval is reduced to zero, the container will be cleared of garbage. If the reference count of a zval is still greater than zero, it will enter the garbage cycle. Second, during a garbage cycle, discover which parts are garbage by checking whether the reference count is reduced by one and checking which variable containers have zero references.
conclusion
Garbage collection mechanism: 2. Use the root buffer mechanism. When PHP finds a zVAL that has a circular reference, it is put into the root buffer. To solve memory leaks caused by circular references (introduced in PHP5.3)