Home  >  Article  >  Backend Development  >  PHP extension and embedding--php memory management_PHP tutorial

PHP extension and embedding--php memory management_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 17:18:44811browse

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);
}
In this example, a PHP function is provided when calling a function. When PHP calls a function, it needs to find the corresponding function in the function table, that is, function_table. Before searching, it must be converted to lowercase letters, which can improve the efficiency of the search. If the function to be called is found through the zend_hash_find function, zend_execute is used to call it. And if haunted is not found, an error message will pop up, indicating that it is not found. But here comes the problem. Note that a lowercase version of the function name string was created before to find the function. This string is used until the zend_hash_find function is used. Once it is not found and an error is reported, the memory space corresponding to this string will inevitably not be found, which causes a memory leak.

Therefore, php provides Zend memory management, Zend memory management is also called ZendMM.
  • Memory management in PHP is similar to the operating system mechanism, but the object is for the memory involved in each request.
  • In addition, ZendMM will also control the memZ specified in the ini file? http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcnlfbGltaXSjrNKyvs3Kx8u10ru1qcO/uPbH68fzy /nSqsfztcTE2rTms6y5/cHL1eK49m1lbW9yeSBsaW1pdKOsxMfDtNKyu+HJ6sfryqew3KGjPGxpPtTazbzW0LXE1+7PwsPmv7S1vcHLy/zT67LZ1/fPtc2zz+DBqs+1tcTSu7LjoaPV67bU stnX98+1zbPW0LXEserXvLXExNq05snqx+u6zcrNt8W1xLe9t6ijrHBocNbQtrzT0LbU06a1xLqvyv2ho9Xi0Km6r8r9sqKyu8rH0ru49rzytaW1xMzmu7ujrMv8w8fW0LD8uqzT0MzYt qi1xNDFz6KjrNTa1eLQqdDFz6K1xLDv1vrPwr7NxNy5u7DRw7+49sfrx/PL+cnqx+u1xMT atOa/6b340NCx6sq2oaPV4tH5vs3E3Lm7yrXP1rbUw7+49sfrx/O1xMTatObH+NPyvfjQ0L fWsfC1xLncwO2hozxsaT7NrMqx1NrNvNbQv7S1vcHL0ru5ssG91tbE2rTmx+vH87XEt73KvaO6cGVyc2lzdGVudLrNcGVyLXJlcXVlc3SjrLbU09pwZXJzaXN0ZW50wLTLtbLu sru24Lj6z7XNs7XEx+vH877N0rvR+cHLo6zSsr7NysfLtcrHtsDBotTaw7/Su7j2x+vH89auz eK1xKOssru74dTax+vH873hyvjWrrrzsbu72MrVoaO1q8rH09DKsbryyse38XBlcnNpc3Rlbn S/ycTc0qpydW50aW1lssXE3NaqtcCjrMv50tTU2tXi1tbH6b /2z8KjrNDo0qrSu7j2ZmxhZ8C01rjKvtXi0ru146GjttTT2srHt/HKx3BlcnNpc3RlbnSjrL340NDE2rTmx+vH87XEt73KvcrHsrvSu9H5tcSho8/Cw+a4+LP2ttTTprnYz7WjugoKPHVs Pgo8bGk+cGVtYWxsb2MoYnVmZmVyX2xlbiwxKSA9PSBtYWxsb2MoYnVmZmVyX2xlbik8bGk+cGVybWFsbG9jKGJ1ZmZlcl9sZW4sMCkgPT0gZW1hbGxvYyhidWZmZXJfbGVuKdXi1t bBqs+1ysfTw7rqtqjS5bXEt73Kvb72tqi1xDxsaT4jZGVmaW5lIHBlbWFsbG9jKHNpemUscGVyc2lzdGVudCkgXDxsaT4KCgo8bGk+Cjx1bD4KKChwZXJzaXN0ZW50KT9tYWxsb2Moc 2l6ZSk6ZW1hbGxvYyhzaXplKSkKCmZsYWc9MbHtyr7Kx3BlcnNpc3RlbnS1xKOszqowse3Kv rK7ysejrL7NuPrSu7DjtcS4vcr009rH68fztcRlbWFsbG9j0rvR+chHLoaMKPGJyPgoKCjxic j4KCs/CzbzW0L/J0tS/tLW9z7XNs7XExNq05snqx+u6r8r90+twaHDW0LXExNq05snqx+u6r8r9tcS21LHI16q7u828o7oKCjxpbWcgc3JjPQ=="http://www.Bkjia.com/uploadfile/Collf iles /20131213/20131213091641239.jpg" alt="">
    If you are not familiar with functions such as malloc, calloc and realloc, please move on: http://www.cppblog.com/sandywin/archive/2011/09/14/155746.html
    In addition, there are two safe mode memory functions: void *safe_emalloc(size_t size, size_t count, size_t addtl);
    void *safe_pemalloc(size_t size, size_t count, size_t addtl, char persistent); The space they apply for is size*count + addtl. The reason for its existence is to avoid int type overflow.


    Next, let’s talk about a more interesting one, reference counting in php:
    Quotes are found in many languages ​​and are used in many situations. Using references saves space because sometimes it is not necessary to make a copy of each variable. The so-called reference counting refers to how many variables refer to the same memory space, so as to avoid possible memory error operations. First look at the following piece of code:
    	* {
    	*     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);
    	* }
    
    This code first declares a zval variable, then initializes it with MAKE_STD_ZVAL, and then uses ZVAL_STRING to attach the initial value. Then for this variable, two variable names are given. The first one is a, the second one is b, and there is no doubt that the second one is definitely a reference. But there must be a problem with this code. The problem is that you did not update the corresponding reference count after using zend_hash_add. zend does not know that you have added such an extra reference, which may result in two releases when releasing the memory.所以经过修改之后的正确代码如下:
    	* {
    	*     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);
    	* }
    
    进行了ZVAL_ADDREF之后,下一次unset变量的时候,会先查看ref_count引用计数,如果=1就释放,如果>1就只是-1,并不进行内存释放。
    Copy on Write 再来看下面的这一段php代码:
    <?php
        $a = 1;
        $b = $a;
        $b += 5;
    ?>
    很显然在第二行的时候b声明了一个a的引用,那么在执行完了第三行的代码之后,b增加了,a增不增加呢?很多时候可能并不想增加。所以这个时候当Zend检测到refCount>1之后,就会执行一个变量分离的操作,把原来的一块内存变成两块内存:
    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;
    }
    首先看到了两个判断语句,第一个判断语句先在符号表里面看看有没有找到相应的变量,如果没找到也就没必要分离了。第二个判断语句是看输入的变量的引用次数是不是小于2,如果是的话那就说明输入变量*varval是唯一的,也没必要分离。 否则的话肯定有引用,这个时候就要制作一个副本varcopy。这个副本会承袭varname对应的值,但是不同之处在于帮它重新申请了内存空间,重新初始化了refcount和is_ref参数。 以a、b为例,在$b+=5,执行之后,b作为varname去寻找是否有引用,发现还有一个引用a,这个时候就把b的值拷出来,然后重新申请一片空间,在重新注册为b。这样的话就是两块独立的内存块了。

    Change on Write 再看一个代码片段:
    <?php
        $a = 1;//执行完这一句之后,a变量的ref_count是1,is_ref是0
        $b = &$a;//这一句之后,变量(zval*)的ref_count是2,然后由于显示的&,is_ref为1
        $b += 5;// 这个时候在执行这一句的时候就不会有任何的分离
    ?>
    如果你觉得想要a跟着b一起改变,那没有问题,只要显式的用&符号进行引用声明就可以了。这样的话is_ref标志位就会被置1. 这时候也就没必要进行内存块的分离了。所以在上面的代码中要把第二个if语句的判断更改一下:
    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;
    ?>
    既不是copy on write也不是change on wirte,那没办法了,只好分离一下。这里只好b独立出来了:







    对php内存管理的一些机制就说到这里,感觉php确实是一门相当神奇的语言。哈哈。

    www.bkjia.comtruehttp://www.bkjia.com/PHPjc/621625.htmlTechArticlephp对内存的管理机制相当的详尽,它在这一点上更类与java的垃圾回收机制。而对于c语言或者c大部分时候都只能由程序员自己把申请的空间...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn