PHP’s memory management mechanism is quite detailed, and it is more similar to Java’s garbage collection mechanism in this regard. For C language or C++, most of the time, the programmer can only release the applied space. In PHP, due to the need to deal with thousands of connections, these connections often need to be maintained for a long time. This is different from the fact that the corresponding memory block will be recycled when the program ends in C.
So it is not enough to just rely on programmers to pay attention to memory recycling when writing programs. PHP must have some internal memory management mechanisms related to connections to ensure that no memory leaks occur.
In this article, we first introduce PHP’s memory mechanism:
Those spatial functions in C language, such as malloc() free() strdup() realloc() calloc(), will have different forms in PHP.
Return the applied memory: For programmers, every piece of applied memory should be returned. Failure to do so will lead to memory leaks. In programs that are not required to run all the time, a small memory leak ends when the entire process is killed. But a web that is always running like apache server, small memory leaks will eventually cause the program to crash.
Example of error handling:
When handling errors, the mechanism generally used is that Zend Engine will set a jump address. Once exit or die or any serious error E_ERROR occurs, a longjmp() will be used to jump to this address. But this approach will almost always lead to memory leaks. Because all free operations will be skipped. (This problem also exists in C++. When designing a class, never write error handling or warning functions in the constructor or destructor. For the same reason, since the object is already in the destruction or creation stage, So any error function handling may interrupt this process, which may lead to memory leaks). The following code gives an example of this:void call_function(const char *fname, int fname_len TSRMLS_DC) { zend_function *fe; char *lcase_fname; /* PHP function names are case-insensitive to simplify locating them in the function tables all function names are implicitly * translated to lowercase */ lcase_fname = estrndup(fname, fname_len);//创造一个函数名的副本 zend_str_tolower(lcase_fname, fname_len);//都转换成小写,这样的寻找的时候很方便,这应该也是php函数表中进行函数标识的方式。 if (zend_hash_find(EG(function_table), lcase_fname, fname_len + 1, (void **)&fe) == FAILURE) {?SUCCESS。这个是要在函数表里面寻找待调用的函数。 zend_execute(fe->op_array TSRMLS_CC); } else { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Call to undefined function: %s()", fname); //等同于Trigger_error() } efree(lcase_fname); }
* { * zval *helloval; * MAKE_STD_ZVAL(helloval); * ZVAL_STRING(helloval, "Hello World", 1); * zend_hash_add(EG(active_symbol_table), "a", sizeof("a"), * &helloval, sizeof(zval*), NULL); * zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), * &helloval, sizeof(zval*), NULL); * }
* { * zval *helloval; * MAKE_STD_ZVAL(helloval); * ZVAL_STRING(helloval, "Hello World", 1); * zend_hash_add(EG(active_symbol_table), "a", sizeof("a"), * &helloval, sizeof(zval*), NULL); * <strong>ZVAL_ADDREF(helloval);</strong>//加上这个之后,就不会有重新释放同一块内存空间这样的错误了 * zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), * &helloval, sizeof(zval*), NULL); * }
<?php $a = 1; $b = $a; $b += 5; ?>
zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC) { zval **varval, *varcopy; if (zend_hash_find(EG(active_symbol_table), varname, varname_len + 1, (void**)&varval) == FAILURE) { /*符号表里没找到 */ return NULL; } if ((*varval)->refcount < 2) { /* varname 是唯一的引用,什么也不用做 */ return *varval; } /* 否则的话,不是唯一的引用,给zval*做一个副本 */ MAKE_STD_ZVAL(varcopy); varcopy = *varval; /* Duplicate any allocated structures within the zval* */ zval_copy_ctor(varcopy); //这一块是怎么拷贝的?mark 应该已经跟varval对应的varname连起来了 /* 把varname的版本删掉,这会减少varval的引用次数 */ zend_hash_del(EG(active_symbol_table), varname, varname_len + 1); /* 初始化新创造的值的引用次数,然后附给varname变量 */ varcopy->refcount = 1; varcopy->is_ref = 0; zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &varcopy, sizeof(zval*), NULL); /* Return the new zval* */ return varcopy; }
<?php $a = 1;//执行完这一句之后,a变量的ref_count是1,is_ref是0 $b = &$a;//这一句之后,变量(zval*)的ref_count是2,然后由于显示的&,is_ref为1 $b += 5;// 这个时候在执行这一句的时候就不会有任何的分离 ?>
if ((*varval)->is_ref || (*varval)->refcount < 2) { /* varname is the only actual reference, * or it's a full reference to other variables * either way: no separating to be done */ return *varval; }
<?php $a = 1; $b = $a; $c = &$a; ?>