In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces "detailed explanation of garbage collection mechanism of PHP5.3". In daily operation, I believe many people have doubts about the detailed explanation of garbage collection mechanism of PHP5.3. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubts of "detailed explanation of garbage collection mechanism of PHP5.3"! Next, please follow the editor to study!
Garbage collection mechanism is a dynamic storage allocation scheme. It automatically releases allocated blocks of memory that are no longer needed by the program. The process of automatically reclaiming memory is called garbage collection. Garbage collection mechanism allows programmers not to care too much about program memory allocation, thus devoting more energy to business logic. In today's popular languages, garbage collection mechanism is a common feature of the new generation of languages, such as Python, PHP, Eiffel, C#, Ruby and so on. Although garbage collection is a popular practice nowadays, it is not too young. It already appeared in the Lisp system developed by MIT in the 1960s, but because of the immature technical conditions at that time, the garbage collection mechanism became a beautiful technology. It was not until the emergence of Java in the 1990s that the garbage collection mechanism was widely used.
PHP also implements dynamic memory management in the language layer, which has been explained in detail in the previous chapter. Dynamic memory management frees developers from tedious memory management. Along with this, PHP also provides a garbage collection mechanism at the language layer, so that programmers do not have to care too much about program memory allocation.
Before the PHP5.3 version, PHP only had simple garbage collection based on reference count. When the reference count of a variable changed to 0, PHP destroyed the variable in memory, but the garbage here could not be called garbage. And PHP releases what the process / thread points at the end of a life cycle, which determines that PHP doesn't need to think too much about memory leaks in the early stages. However, with the development of PHP, the increase of PHP developers and the expansion of its business scope, a more perfect garbage collection mechanism has been introduced into PHP5.3. The new garbage collection mechanism solves the problem of reference memory leaks that cannot handle loops. The garbage collection mechanism in PHP5.3 uses the synchronization algorithm in synchronous cycle recovery (Concurrent Cycle Collection in Reference Counted Systems) in the article citation counting system. We will not repeat the introduction of this algorithm, which is illustrated in the official document of PHP: recycling cycle (Collecting Cycles).
As mentioned earlier, in PHP, the main means of memory management is reference counting, and the purpose of introducing a garbage collection mechanism is to break circular references in reference counting so as to prevent memory leaks caused by this. The garbage collection mechanism is based on PHP's dynamic memory management. In order to introduce garbage collection mechanism, PHP5.3 has made some changes in the basic structure of variable storage, as follows:
The copy code is as follows:
Struct _ zval_struct {
/ * Variable information * /
Zvalue_value value; / * value * /
Zend_uint refcount__gc
Zend_uchar type; / * active type * /
Zend_uchar is_ref__gc
}
Compared to previous versions of PHP5.3, both the reference count field refcount and the reference field is_ref have been followed by _ _ gc for the new garbage collection mechanism. In the source style of PHP, a large number of macros is a very distinct feature. These macros are equivalent to an interface layer, which shields some underlying implementations below the interface layer, such as the ALLOC_ ZVAL macro. Before PHP5.3, this macro directly called PHP's memory management allocation function emalloc to allocate memory, and the amount of memory allocated was determined by the type of variable. After introducing the garbage collection mechanism, the ALLOC_ ZVAL macro directly adopts the new garbage collection unit structure, and the size allocated is all the same, all the memory occupied by the zval_gc_info structure, and after allocating memory, initialize the garbage collection mechanism of this structure. The code is as follows:
The copy code is as follows:
/ * The following macroses override macroses from zend_alloc.h * /
# undef ALLOC_ZVAL
# define ALLOC_ZVAL (z)\
Do {\
(Z) = (zval*) emalloc (sizeof (zval_gc_info));\
GC_ZVAL_INIT (z);\
} while (0)
The zend_gc.h file is referenced in line 749 of zend.h: # include "zend_gc.h" thus replacing macros such as ALLOC_ZVAL in the zend_alloc.h file referenced on line 237. in the new macro, the key change is the change in the allocated memory size and content, adding garbage collection mechanism to the previous pure memory allocation, all of which are included in the zval_gc_info structure:
The copy code is as follows:
Typedef struct _ zval_gc_info {
Zval z
Union {
Gc_root_buffer * buffered
Struct _ zval_gc_info * next
} u
} zval_gc_info
For any variable stored in the ZVAL container, a zval structure is assigned, which ensures that it aligns with the beginning of the memory allocated with the zval variable, so that it can be used as a zval when the pointer of the zval_gc_info type is cast. There is a union after the zval field: U. U includes the buffered field of the gc_root_buffer structure and the next field of the zval_gc_info structure. One of these two fields represents the root node of the garbage collection mechanism cache, and the other is the next node of the zval_gc_info list. The garbage collection mechanism cache node can be reflected here, whether as a root node or a list node. After ALLOC_ZVAL allocates memory, it calls GC_ZVAL_INIT to initialize the zval_gc_info that replaces zval. It sets the buffered field of member u in zval_gc_info to NULL, which has a value only when it is placed in the garbage collection buffer, otherwise it will always be NULL. Since all variables in PHP exist in the form of zval variables, zval is replaced by zval_gc_info here, thus successfully realizing the integration of garbage collection mechanism in the original system.
PHP's garbage collection mechanism is enabled by default in PHP5.3, but we can set it to disable directly through the configuration file, and the corresponding configuration field is: zend.enable_gc. This field is not available by default in the php.ini file, and if we need to disable this feature, add zend.enable_gc=0 or zend.enable_gc=off to the php.ini. In addition to modifying the php.ini configuration zend.enable_gc, you can also turn the garbage collection mechanism on / off by calling the gc_enable () / gc_disable () function. The call effect of these functions is the same as modifying the configuration item to turn the garbage collection mechanism on or off. In addition to these two functions, PHP provides the gc_collect_cycles () function to force periodic recycling when the root buffer is not full. There are some operations and fields related to whether the garbage collection mechanism is enabled or not in the PHP source code. You have the following code in the zend.c file:
The copy code is as follows:
Static ZEND_INI_MH (OnUpdateGCEnabled) / * {{* /
{
OnUpdateBool (entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC)
If (GC_G (gc_enabled)) {
Gc_init (TSRMLS_C)
}
Return SUCCESS
}
/ *}}
ZEND_INI_BEGIN ()
ZEND_INI_ENTRY ("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
STD_ZEND_INI_BOOLEAN ("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
# ifdef ZEND_MULTIBYTE
STD_ZEND_INI_BOOLEAN ("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals)
# endif
ZEND_INI_END ()
The corresponding operation function of zend.enable_gc is ZEND_INI_MH (OnUpdateGCEnabled). If the garbage collection mechanism is enabled, that is, if GC_G (gc_enabled) is true, the gc_init function will be called to initialize the garbage collection mechanism. The gc_init function is on line zend/zend_gc.c 121. this function determines whether the garbage collection mechanism is enabled. If it is enabled, the whole mechanism is initialized, that is, call malloc directly to allocate 10000 gc_root_buffer memory space to the entire cache list. 10000 of this is hard-coded in the code and exists as the macro GC _ ROOT_BUFFER_MAX_ENTRIES. If you need to modify this value, you need to modify the source code and recompile the PHP. After pre-allocating memory, the gc_init function calls the gc_reset function to reset some global variables used in the whole mechanism, such as setting the count of times to run in gc (gc_runs) and the number of garbage in gc (collected) to 0, setting the previous node and the next node of the two-way link header node pointing to itself, and so on. In addition to some of the global variables mentioned for the garbage collection mechanism, there are other variables that are widely used, which are described in part as follows:
The copy code is as follows:
Typedef struct _ zend_gc_globals {
Zend_bool gc_enabled; / * whether to enable garbage collection mechanism * /
Zend_bool gc_active; / * whether it is in progress * /
Gc_root_buffer * buf; / * pre-allocated buffer array. Default is 10000 (preallocated arrays of buffers) * /
The root node of the gc_root_buffer roots; / * list (list of possible roots of cycles) * /
Gc_root_buffer * unused; / * list of unused buffers (list of unused buffers) * /
Gc_root_buffer * first_unused; / * points to first unused buffer node (pointer to first unused buffer) * /
Gc_root_buffer * last_unused; / * points to the last unused buffer node, where the tag ends with (pointer to last unused buffer) * /
Zval_gc_info * zval_to_free; / * temporary list of zval variables to be released (temporaryt list of zvals to free) * /
Zval_gc_info * free_list; / * temporary variable, at the beginning of the list to be released * /
Zval_gc_info * next_to_free; / * temporary variable, location of the next variable to be released * /
Zend_uint gc_runs; / * Statistics on the number of times gc runs * /
Number of garbage in zend_uint collected; / * gc * /
/ / omit.
}
When we use a unset operation to clear the memory occupied by this variable (probably just the reference count minus one), the item corresponding to the variable name is deleted from the hash table of the current symbol, after all the operations are performed, and a destructor is called on the item deleted from the symbol table, the temporary variable calls zval_dtor, and the normal variable calls zval_ptr_dtor.
Of course, we can't find the unset function in the function set of PHP because it is a language structure. The corresponding intermediate code is ZEND_UNSET, and you can find the implementation associated with it in the Zend/zend_vm_execute.h file.
Zval_ptr_dtor is not a function, just a macro that looks a bit like a function. In the Zend/zend_variables.h file, this macro points to the function _ zval_ptr_dtor. On line 424 of Zend/zend_execute_API.c, the function-related code is as follows:
The copy code is as follows:
ZEND_API void _ zval_ptr_dtor (zval * zval_ptr ZEND_FILE_LINE_DC) / * {{* /
{
# if DEBUG_ZEND > = 2
Printf ("Reducing refcount for% x (% x):% d->% d\ n", * zval_ptr, zval_ptr, Z_REFCOUNT_PP (zval_ptr), Z_REFCOUNT_PP (zval_ptr)-1)
# endif
Z_DELREF_PP (zval_ptr)
If (Z_REFCOUNT_PP (zval_ptr) = = 0) {
TSRMLS_FETCH ()
If (* zval_ptr! = & EG (uninitialized_zval)) {
GC_REMOVE_ZVAL_FROM_BUFFER (* zval_ptr)
Zval_dtor (* zval_ptr)
Efree_rel (* zval_ptr)
}
} else {
TSRMLS_FETCH ()
If (Z_REFCOUNT_PP (zval_ptr) = = 1) {
Z_UNSET_ISREF_PP (zval_ptr)
}
GC_ZVAL_CHECK_POSSIBLE_ROOT (* zval_ptr)
}
}
/ *}}
We can clearly see the deconstruction process of this zval from the code, and we have done the following two operations on the reference count field:
If the reference count of the variable is 1, that is, minus one, the reference count is 0, and the variable is cleared directly. If the current variable is cached, you need to clear the cache. If the reference count of the variable is greater than 1, that is, the reference count is greater than 0 after minus one, the variable is put into the garbage list. If there is a reference to the change, remove its reference.
The operation to put variables into the garbage list is GC_ZVAL_CHECK_POSSIBLE_ROOT, which is also a macro that corresponds to the function gc_zval_check_possible_root, but this function only performs garbage collection on arrays and objects. For array and object variables, it calls the gc_zval_possible_root function.
The copy code is as follows:
ZEND_API void gc_zval_possible_root (zval * zv TSRMLS_DC)
{
If (UNEXPECTED (GC_G (free_list)! = NULL & &
GC_ZVAL_ADDRESS (zv)! = NULL & &
GC_ZVAL_GET_COLOR (zv) = = GC_BLACK) & &
(GC_ZVAL_ADDRESS (zv)
< GC_G(buf) || GC_ZVAL_ADDRESS(zv) >= GC_G (last_unused)) {
/ * The given zval is a garbage that is going to be deleted by
* currently running GC * /
Return
}
If (zv- > type = = IS_OBJECT) {
GC_ZOBJ_CHECK_POSSIBLE_ROOT (zv)
Return
}
GC_BENCH_INC (zval_possible_root)
If (GC_ZVAL_GET_COLOR (zv)! = GC_PURPLE) {
GC_ZVAL_SET_PURPLE (zv)
If (! GC_ZVAL_ADDRESS (zv)) {
Gc_root_buffer * newRoot = GC_G (unused)
If (newRoot) {
GC_G (unused) = newRoot- > prev
} else if (GC_G (first_unused)! = GC_G (last_unused)) {
NewRoot = GC_G (first_unused)
GC_G (first_unused) + +
} else {
If (! GC_G (gc_enabled)) {
GC_ZVAL_SET_BLACK (zv)
Return
}
Zv- > refcount__gc++
Gc_collect_cycles (TSRMLS_C)
Zv- > refcount__gc--
NewRoot = GC_G (unused)
If (! newRoot) {
Return
}
GC_ZVAL_SET_PURPLE (zv)
GC_G (unused) = newRoot- > prev
}
NewRoot- > next = GC_G (roots). Next
NewRoot- > prev = & GC_G (roots)
GC_G (roots). Next-> prev = newRoot
GC_G (roots). Next = newRoot
GC_ZVAL_SET_ADDRESS (zv, newRoot)
NewRoot- > handle = 0
NewRoot- > u.pz = zv
GC_BENCH_INC (zval_buffered)
GC_BENCH_INC (root_buf_length)
GC_BENCH_PEAK (root_buf_peak, root_buf_length)
}
}
}
As mentioned earlier, the gc_zval_check_possible_root function only performs garbage collection on arrays and objects, while in the gc_zval_possible_root function, variables of the object type call the GC_ZOBJ_CHECK_POSSIBLE_ROOT macro. The call procedure for other variable types of mechanisms that can be used for garbage collection is as follows:
Check whether the zval node information has been put into the node buffer, and if it has been put into the node buffer, it will be returned directly, which can optimize its performance. Then the object node is processed and returned directly, and the later operation is not performed to determine whether the node has been marked purple, and if it is purple, it is no longer added to the node buffer. Here is to ensure that a node only performs the operation added to the buffer once.
Mark the color of the node as purple, indicating that the node has been added to the buffer. You don't have to add it next time.
Find out the location of the new node and perform a garbage collection operation if the buffer is full.
Adds a new node to the bidirectional linked list where the buffer is located.
In the gc_zval_possible_root function, when the buffer is full, the program calls the gc_collect_cycles function to perform garbage collection. The most critical steps are:
Line 628 here is step B of the algorithm in its official documentation, which uses a depth-first search to find all possible roots and subtracts the reference count from each variable container by 1. To ensure that the same variable container is not subtracted by "1" twice, the algorithm is marked with gray that has been reduced by 1.
Line 629 this is step C of the algorithm, which once again uses a depth-first search for each root node to check the reference count of each variable container. If the reference count is 0, the variable container is marked with white. If the number of references is greater than 0, the operation of subtracting the reference count by 1 using a depth-first search at this point is resumed (that is, the reference count is increased by 1), and then they are re-marked with black.
In the final step D of the line 630 algorithm, the algorithm traverses the root buffer to remove the variable container root (zval roots) from there, while checking to see if there are any variable containers marked white in the previous step. Each variable container marked with white is cleared. In [gc_collect_cycles ()-> gc_collect_roots ()-> zval_collect_white ()], we can see that nodes marked with white are added to the list of global variables zval_to_free. This list is useful in later operations.
PHP's garbage collection mechanism marks the status in four colors during execution.
GC_WHITE white indicates garbage
GC_PURPLE purple indicates that it has been placed in the buffer
GC_GREY gray indicates that a minus one operation for refcount has been carried out.
GC_BLACK black is the default color, normal
The related tags and operation codes are as follows:
The copy code is as follows:
# define GC_COLOR 0x03
# define GC_BLACK 0x00
# define GC_WHITE 0x01
# define GC_GREY 0x02
# define GC_PURPLE 0x03
# define GC_ADDRESS (v)\
((gc_root_buffer*) (zend_uintptr_t) (v)) & ~ GC_COLOR))
# define GC_SET_ADDRESS (v, a)\
(v) = ((gc_root_buffer*) (zend_uintptr_t) (v)) & GC_COLOR) | ((zend_uintptr_t) (a)
# define GC_GET_COLOR (v)\
(zend_uintptr_t) (v)) & GC_COLOR)
# define GC_SET_COLOR (v, c)\
(v) = ((gc_root_buffer*) (zend_uintptr_t) (v)) & ~ GC_COLOR) | (c)
# define GC_SET_BLACK (v)\
(v) = ((gc_root_buffer*) ((zend_uintptr_t) (v)) & ~ GC_COLOR))
# define GC_SET_PURPLE (v)\
(v) = ((gc_root_buffer*) ((zend_uintptr_t) (v)) | GC_PURPLE))
At this point, the study of "detailed explanation of garbage collection mechanism of PHP5.3" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.