Before starting, let's take a look at a simple piece of code:
Copy codeThe Code is as follows:
<? Php // Example 1
$ Foo = 1;
$ Bar = $ foo;
Echo $ foo + $ bar;
?>
Execute this code to print the number 2. From the memory perspective, we can analyze the Code as follows: allocate a piece of memory to the foo variable, store 1 in it, and assign a piece of memory to the bar variable, also save a 1, and finally calculate the result output. In fact, we found that the foo and bar variables can use the same memory because of the same value. In this way, memory usage is reduced by 1, and, it also saves the computing overhead of allocating memory and managing memory addresses. Yes, many systems that involve memory management have implemented this shared memory policy with the same value: Write-time Replication
Most of the time, we may fear their concepts due to some terms. In fact, their basic principles are often very simple. This section describes how to copy this policy when writing in PHP:
There are many application scenarios for Copy on Write (COW). For example, the optimization of memory usage in process replication in Linux, in various programming languages, for example, C ++ STL has similar applications. COW is a common optimization method and can be classified as: Resource delay allocation. Resources are occupied only when resources are actually needed. Copying During writing can usually reduce resource usage.
Note: To save space, COW will be used in the following sections to indicate "copy upon writing ";
Optimization of deferred memory Replication
As mentioned above, the COW in PHP can be simply described as: If a value is assigned to a variable by assignment, a new memory will not be applied to store the value saved by the new variable, instead, a counter is used to share the memory. A new space is requested only when a reference pointing to a variable value changes to save the value content to reduce memory usage. In many scenarios, PHP uses COW for memory optimization. For example, variable values are assigned multiple times, function parameter passing, and real parameters are modified in the function body.
Let's take a look at the example of memory display, which makes it easier to see the obvious role of COW in memory usage optimization:
Copy codeThe Code is as follows:
<? Php // Example 2
$ J = 1;
Var_dump (memory_get_usage ());
$ Tipi = array_fill (0, 100000, 'php-internal ');
Var_dump (memory_get_usage ());
$ Tipi_copy = $ tipi;
Var_dump (memory_get_usage ());
Foreach ($ tipi_copy as $ I ){
$ J + = count ($ I );
}
Var_dump (memory_get_usage ());
// ----- Execution result -----
$ Php t. php
Int (630904)
Int (10479840)
Int (10479944)
Int (10480040)
The code above highlights the role of COW. When the array variable $ tipi is assigned to $ tipi_copy, the memory usage does not increase by half immediately, the number of cyclically traversed $ tipi_copy does not change significantly. Here, the data of $ tipi_copy and $ tipi both point to the same memory and are not copied.
That is to say, even if we do not use references, after a variable is assigned a value, as long as we do not change the value of the variable, we will not apply for memory to store data. Based on this, we can easily think of some scenarios where COW can effectively control memory usage: Only variables are used for calculation, but few modifications are made to them, such as passing function parameters, copying large arrays and so on without changing the variable value.
Value of change in replication Separation
Multiple variables with the same value share the same memory, which indeed saves memory space, but the value of the variable changes. In the preceding example, if the value pointing to the same memory changes (or changes may occur), you need to "detach" The changed value. This "detach" operation is "copy ".
In PHP, the Zend engine introduces two variables ref_count and is_ref to identify whether the same zval address is shared by multiple variables:
Copy codeThe Code is as follows:
Ref_count and is_ref are defined in the zval struct (see section 1 of chapter 1)
Is_ref indicates whether the user uses & mandatory reference;
Ref_count is the reference count used to identify the number of variables referenced by zval, that is, the automatic reference of COW. If it is 0, it will be destroyed;
For more information about these two variables, see Chapter 3 Section 6: Variable assignment and destruction.
Note: it can be seen that $ a = $ B; and $ a = & $ B; there is no difference in the use of memory in PHP (when the value does not change );
Next, let's make a slight change in Example 2: If the value of $ copy changes, what will happen? :
Copy codeThe Code is as follows:
<? Php // Example 3
// $ Tipi = array_fill (0, 3, 'php-internal ');
// Array_fill is no longer used here. Why?
$ Tipi [0] = 'php-internal ';
$ Tipi [1] = 'php-internal ';
$ Tipi [2] = 'php-internal ';
Var_dump (memory_get_usage ());
$ Copy = $ tipi;
Xdebug_debug_zval ('tipi ', 'copy ');
Var_dump (memory_get_usage ());
$ Copy [0] = 'php-internal ';
Xdebug_debug_zval ('tipi ', 'copy ');
Var_dump (memory_get_usage ());
// ----- Execution result -----
$ Php t. php
Int (629384)
Tipi: (refcount = 2, is_ref = 0) = array (0 => (refcount = 1, is_ref = 0) = 'php-internal ',
1 => (refcount = 1, is_ref = 0) = 'php-internal ',
2 => (refcount = 1, is_ref = 0) = 'php-internal ')
Copy: (refcount = 2, is_ref = 0) = array (0 => (refcount = 1, is_ref = 0) = 'php-internal ',
1 => (refcount = 1, is_ref = 0) = 'php-internal ',
2 => (refcount = 1, is_ref = 0) = 'php-internal ')
Int (629512)
Tipi: (refcount = 1, is_ref = 0) = array (0 => (refcount = 1, is_ref = 0) = 'php-internal ',
1 => (refcount = 2, is_ref = 0) = 'php-internal ',
2 => (refcount = 2, is_ref = 0) = 'php-internal ')
Copy: (refcount = 1, is_ref = 0) = array (0 => (refcount = 1, is_ref = 0) = 'php-internal ',
1 => (refcount = 2, is_ref = 0) = 'php-internal ',
2 => (refcount = 2, is_ref = 0) = 'php-internal ')
Int (630088)
In this example, we can find the following features:
$ Copy = $ tipi; this basic assignment operation triggers COW memory "sharing" and does not produce memory replication;
The COW granularity is the zval structure, and all the variables in PHP are based on zval. Therefore, the scope of COW is all the variables. For a set composed of zval struct (such as arrays and objects ), when you need to copy the memory, the complex objects are decomposed into the minimum granularity for processing. In this way, when a part of a complex object in the memory is modified, all the elements of the object are not "separated and copied" to produce a memory copy;
Copy codeThe Code is as follows:
The COW policy is also used when array_fill () is filled with arrays, which may affect the demo of this example. For more information, see $ PHP_SRC/ext/standard/array. implementation of PHP_FUNCTION (array_fill) in c.
Xdebug_debug_zval () is a function in the xdebug extension used to output reference information of variables within zend. If you have not installed the xdebug extension, you can use debug_zval_dump () instead. Reference: http://www.php.net/manual/zh/function.debug-zval-dump.php
Implement write-time Replication
After reading the above three examples, I believe you can also understand the implementation principle of COW in PHP: the COW in PHP is implemented based on reference count ref_count and is_ref, with one more variable pointer, add ref_count to 1, and then subtract 1 from 0 to destroy it. Similarly, add 1 to is_ref for one more forced reference, and subtract 1 from the other.
Here is a typical example:
Copy codeThe Code is as follows:
<? Php // Example 4
$ Foo = 1;
Xdebug_debug_zval ('foo ');
$ Bar = $ foo;
Xdebug_debug_zval ('foo ');
$ Bar = 2;
Xdebug_debug_zval ('foo ');
?>
// ----- Execution result -----
Foo: (refcount = 1, is_ref = 0) = 1
Foo: (refcount = 2, is_ref = 0) = 1
Foo: (refcount = 1, is_ref = 0) = 1
As described in the Variable Section, we know that when $ foo is assigned a value, the value of the $ foo variable is directed only by the $ foo variable. When $ foo is assigned to $ bar, PHP does not copy the memory to $ bar, but points $ foo and $ bar to the same address. At the same time, the reference count is increased by 1, that is, the new 2. Then, we changed the value of $ bar. If the $ bar variable is directly required to point to the memory, the value of $ foo will also change. This is not the expected result. Therefore, the PHP kernel copies the memory and updates the value to the assigned value: 2 (this operation is also called the variable separation operation ), at the same time, the original $ foo variable points to only $ foo, so the reference count is updated to refcount = 1.
It looks simple, but the existence of the & operator makes the actual situation much more complicated. See the following example:
Figure 6.6 & memory replication separation caused by operators>
From this example, we can see that PHP is prone to problems with the & operator: When $ beauty = & $ pan;, both variables are essentially changed to reference types, as a result, the common variable $ pan appears to have the same behavior as & $ pan in some internal processing, especially when reference variables are used in array elements, which can easily cause problems. (See the final example)
Most of PHP's work is text processing, while variables are carriers. The use of different types of variables runs through the lifecycle of PHP, the COW policy of the variable also reflects the Zend engine's processing of the variable and its memory. For details, see the content related to the source code file:
Copy codeThe Code is as follows:
Zend/zend_execute.c
==========================================================
Zend_assign_to_variable_reference ();
Zend_assign_to_variable ();
Zend_assign_to_object ();
Zend_assign_to_variable ();
// Use the following macro definitions
Zend/zend. h
==========================================================
# Define Z_REFCOUNT (z) Z_REFCOUNT_P (& (z ))
# Define Z_SET_REFCOUNT (z, rc) Z_SET_REFCOUNT_P (& (z), rc)
# Define Z_ADDREF (z) Z_ADDREF_P (& (z ))
# Define Z_DELREF (z) Z_DELREF_P (& (z ))
# Define Z_ISREF (z) Z_ISREF_P (& (z ))
# Define Z_SET_ISREF (z) Z_SET_ISREF_P (& (z ))
# Define Z_UNSET_ISREF (z) Z_UNSET_ISREF_P (& (z ))
# Define Z_SET_ISREF_TO (z, isref) Z_SET_ISREF_TO_P (& (z), isref)
Finally, please use the reference with caution &
The reference is not the same as the reference count of the variable mentioned above and the reference in PHP. The reference is similar to the pointer in C. They can access the same content through different labels, however, PHP references only simple variable aliases, without the flexibility and restrictions of C commands.
PHP has a lot of unexpected behaviors. For some historical reasons, you can choose not to fix them because it cannot damage the compatibility, or some use cases are relatively small. In PHP, you can only try to avoid these traps. The following is an example.
Since the reference operator will lead to php cow policy optimization, you also need to have a clear understanding of the referenced behavior to avoid misuse and avoid some bugs that are hard to understand. If you think you are familiar with the reference in PHP, try to explain the following example:
Copy codeThe Code is as follows:
<? Php
$ Foo ['love'] = 1;
$ Bar = & $ foo ['love'];
$ Tipi = $ foo;
$ Tipi ['love'] = '2 ';
Echo $ foo ['love'];
In this example, 2 is output at the end. We are surprised how $ tipi affects the reference operation of $ foo and $ bar variables, which turns the pollution of $ foo ['love'] into reference, therefore, Zend does not modify $ tipi ['love'] to generate memory replication splitting.