Before proceeding, if you do not understand what GC (GarbageCollection) is, you must first understand GC. Otherwise, you do not know what I am talking about! Links: www. php. netmanualenfeatures. gc. phpblog. csdn. netphpkernelarticledetails5721343
Before proceeding, if you do not understand what GC is, you must first understand GC. Otherwise, you do not know what I am talking about! Links: http://www.php.net/manual/en/features.gc.php http://blog.csdn.net/phpkernel/article/details/5734743 ==============================
Before proceeding, if you do not understand what GC is, you must first understand GC. Otherwise, you do not know what I am talking about!
Links:
Http://www.php.net/manual/en/features.gc.php
Http://blog.csdn.net/phpkernel/article/details/5734743
========================================================== ======================================
Let's talk about the problem in detail:
In unix, php commands are used to execute php scripts. Is there a chance to recycle the memory before php ends? Is there a chance to call the new GC algorithm?
This problem occurs because there is an offline data import script online, and tens of millions of rows of data need to be filtered into the database. It is found that the execution process reaches a certain level, the memory usage exceeds the maximum value.
1 |
Fatal error: Allowed memory size of 293601280 bytes exhausted
|
The first thought was whether the program had any bugs that caused the memory to exceed. After reading the program for a long time, no problems were found, so the question suddenly occurred. To solve this problem, the best way is to go through the source code.
Previously I said:
As we all know, PHP5.3 has a new garbage collection mechanism: GC, which is not the focus, not to mention its principles.
After reading the PHP source code, we found that the call time is in the main/main. c: php_request_shutdown function,
1 2 |
/* 7. Shutdown scanner/executor/compiler and restore ini entries */
zend_deactivate(TSRMLS_C);
|
Php_request_shutdown: the name indicates that it is executed at the end of the php request. gc_collect_cycles is executed here to clean up the memory.
In fact, this sentence is correct, but it is only for the SAPI interface (I was wrong here before .), This php_request_shutdown will not be executed when you run the PHP script using the php Command.
Back to the initial question, is there any GC execution in the process?
To learn the answer more intuitively and effectively, I chose the most BT and brute-force method to intercept gc_collect_cycles and output error_log to the file. As long as it is executed,
It will certainly output logs.
After PHP is re-compiled, it is clear that, as long as the buckets is full and exceeds the default value of 1000, GC will be started to clean up useless memory to prevent memory leakage.
Ask "when to trigger GC ?", Answer: "When the number of buckets exceeds 1000," it's not a waste of words. What we need is a real execution process, so .. Continuous debugging,
Continuous grep, continuous step, continuous C + T, finally figured out. The following is a detailed explanation of how PHP is triggered.
Note that the PHP Command entry is different from the sapi entry, and I will load it here, so I thought it was common.
The test code uses the official document as an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Foo
{
public $var = '3.1415962654' ;
}
for ( $i = 0; $i <= 1000000; $i ++ )
{
$a = new Foo;
$a ->self = $a ;
}
echo memory_get_peak_usage(), "\n" ;
?>
|
Such code may cause a large amount of memory leakage before PHP5.3. However, who can develop such abnormal code during development? Unless this person is abnormal. ^ .*
What is the PHP Command entry? What is the process?
The main function process is as follows:
Main function (sapi/cli/php_cli.c) = php_execute_script (main/main. c) ==> zend_execute_scripts (Zend/zend. c) ==> execute (Zend/zend_vm_execute.h)
The place where GC is called is in execute.
Briefly describe this process,
Main is the entry, and its function is to make different settings based on the parameters we pass. Finally, we will pass our php script as a zend_file_handle pointer
In the php_execute_script function, zend_file_handle encapsulates FILE * and saves some other FILE information.
Php_execute_script checks some files and adds the php script to the included_files hash table.
Php_execute_scripts will execute the zend_compile_file function to explain the PHP code we have written, and finally execute.
Zend generates op code after parsing the script and saves it to the hash table active_op_array. execute Executes each op one by one,
Op basically corresponds to a macro like ZEND_ASSIGN _ * _ HANDLER, which is stored in active_op_array-> opline-> handlers.
After entering execute:
First initialize execute_data, which saves a lot of important information and context information, and then calls the ZEND_VM_SET_OPCODE macro,
Direct the execute_data-> opline pointer to active_op_array-> opline-> handlers.
Execute will execute a while loop and execute opline one by one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
while (1) {
int ret;
#ifdef ZEND_WIN32
if (EG(timed_out)) {
zend_timeout(0);
}
#endif
if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) {
switch (ret) {
case 1:
EG(in_execution) = original_in_execution;
return ;
case 2:
op_array = EG(active_op_array);
goto zend_vm_enter;
case 3:
execute_data = EG(current_execute_data);
default :
break ;
}
}
}
|
Each handlers will execute a macro: ZEND_VM_NEXT_OPCODE (), which means to jump to the next Opline, so that it can be executed one by one.
The above PHP code will be tracked to execute the ZEND_ASSIGN_SPEC_CV_VAR_HANDLER macro. What does it do? Variable assignment
The following code performs the following operations:
1 2 3 4 |
class A{
}
$a = new A();
|
This macro will be executed here.
There is a code segment in this macro:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
static int ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zend_op *opline = EX(opline);
zend_free_op free_op2;
zval *value = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC);
zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_W TSRMLS_CC);
if (IS_CV == IS_VAR && !variable_ptr_ptr) {
if (zend_assign_to_string_offset(&EX_T(opline->op1.u.var), value, IS_VAR TSRMLS_CC)) {
if (!RETURN_VALUE_UNUSED(&opline->result)) {
EX_T(opline->result.u.var).var.ptr_ptr = &EX_T(opline->result.u.var).var.ptr;
ALLOC_ZVAL(EX_T(opline->result.u.var).var.ptr);
INIT_PZVAL(EX_T(opline->result.u.var).var.ptr);
ZVAL_STRINGL(EX_T(opline->result.u.var).var.ptr, Z_STRVAL_P(EX_T(opline->op1.u.var).str_offset.str)+EX_T(opline->op1.u.var).str_offset.offset, 1, 1);
}
} else if (!RETURN_VALUE_UNUSED(&opline->result)) {
AI_SET_PTR(EX_T(opline->result.u.var).var, EG(uninitialized_zval_ptr)); PZVAL_LOCK(EG(uninitialized_zval_ptr));
}
} else {
value = zend_assign_to_variable(variable_ptr_ptr, value, 0 TSRMLS_CC);
if (!RETURN_VALUE_UNUSED(&opline->result)) {
AI_SET_PTR(EX_T(opline->result.u.var).var, value);
PZVAL_LOCK(value);
}
}
/* zend_assign_to_variable() always takes care of op2, never free it! */
if (free_op2.var) {zval_ptr_dtor(&free_op2.var);};
ZEND_VM_NEXT_OPCODE();
}
|
Free_op2.var stores the object of new.
Free_op2.var where did this come from?
Maintain an execute_data structure during the entire execute period, which contains a Ts pointer
1 |
union _temp_variable *Ts;
|
It is used to save some temporary variable information, such as new A (), which will be saved to the Ts linked list,
Opline-> op2.u. var stores the location of the temporary variable, and Ts + the value is A zval * pointer, which stores the object generated by new.
In the code
1 |
if (free_op2.var) {zval_ptr_dtor(&free_op2.var);};
|
Zval_ptr_dtor is executed to the Zend/zend_execute_API.c: _ zval_ptr_dtor function based on the free_op2.var value,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */
{
zval *zv = *zval_ptr;
#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_P(zv);
if (Z_REFCOUNT_P(zv) == 0) {
TSRMLS_FETCH();
if (zv != &EG(uninitialized_zval)) {
GC_REMOVE_ZVAL_FROM_BUFFER(zv);
zval_dtor(zv);
efree_rel(zv);
}
} else {
TSRMLS_FETCH();
if (Z_REFCOUNT_P(zv) == 1) {
Z_UNSET_ISREF_P(zv);
}
GC_ZVAL_CHECK_POSSIBLE_ROOT(zv);
}
}
|
GC_ZVAL_CHECK_POSSIBLE_ROOT (zv );
This is where the final GC algorithm is executed.
Gc_collect_cycles is executed in this macro ..
So ..
Back to the above question,
Php will execute the GC algorithm to recycle junk memory no matter on the SAPI interface or command side.
It seems that the problem of Memory leakage has to be found in the program.
========================================================== ======================================
If you find something wrong, you must point it out for me. 3Q!
Http://www.imsiren.com/archives/704