Extending.and.Embedding.PHP读书笔记(5)-内存管理

1.内存

php对照c语言的malloc(), free(), strdup(), realloc(), calloc()等等这些函数进行了封装,实现了一套自己的内存管理宏emalloc(),efree(),strdup(),erealoc(),ecalloc()等等宏。为什么要自行封装内存管理函数呢,直接用操作系统提供的不行吗?这是因为在每个请求中,遇到die,exit或者是具有使脚本停止执行的错误,就会跳过请求结束后对请求所使用资源的清理那一步(这一步对用户是透明的),如果使用原生的内存管理函数,就会出现该请求申请的内存没有被释放的情况,也就出现了内存泄露。但是php自身的 Zend Memory Management(ZendMM Zend内存管理)层,则可以保证在脚本异常停止后的内存释放。
有时候可能我们需要申请一些长久使用的内存,所以php又定义了另外一套pmalloc的宏,只不过其最后加了一个参数 char persistent,当这个参数为1时,则使用malloc,如果为0,则使用emalloc.当然efree和free这两个函数绝对不能混用,否则会出现错误.

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);
上面这两个函数则对要申请的内存进行了数据的溢出检查,当参数count的值大于操作系统可表达的最大整数值时,返回错误,而普通函数的则会返回一块错误大小的内存.

2.变量引用数
首先,在zend engine中,php的变量和变量的值是两个不同的概念,变量值是通过zavl*这种匿名的方式来存取的,而变量名则是直接使用名字来存取的.
<?php
    $a = 'Hello World';
    $b = $a;
    unset($a);
?>
上面的代码第二行,php并不会再去重新分配内存给变量b,而是直接使a所指向的值结构中的recount加1.
在php的父语言c中的代码表示如下:
{
    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);
    ZVAL_ADDREF(helloval);
    zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),
                                           &helloval, sizeof(zval*), NULL);
}
而第三行仅仅是使那个值得refcount减一.

那么下面的代码是如何操作的呢?
<?php
    $a = 1;
    $b = $a;
    $b += 5;
?>
根据上面的结论,这时$b += 5势必也会改变$a的值.
这里php则重新分配了一块内存来存储变量b,并执行操作.
其实现如下
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) {
       /* Variable doesn't actually exist  fail out */
       return NULL;
   }
   if ((*varval)->refcount < 2) {
       /* varname is the only actual reference,
        * no separating to do
        */
       return *varval;
   }
   /* Otherwise, make a copy of the zval* value */
   MAKE_STD_ZVAL(varcopy);
   varcopy = *varval;
   /* Duplicate any allocated structures within the zval* */
   zval_copy_ctor(varcopy);

   /* Remove the old version of varname
    * This will decrease the refcount of varval in the process
    */
   zend_hash_del(EG(active_symbol_table), varname, varname_len + 1);

   /* Initialize the reference count of the
    * newly created value and attach it to
    * the varname variable
    */
   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;
    $b = &$a;
    $b += 5;
?>
我们希望的结果是$a和$b都是6,但是根据上面的结果,$b将会指向一块新的内存.不过在赋值的时候,多了一个"&"符号.这个符号提示php这是引用传值.因此变量a指向的那个zval的is_ref值被置为1
在上面的get_var_and_separate()函数中,更改如下:
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;
?>
首先会申请一块内存zavl*来存储值1,其他属性值分别为 type=IS_LONG value.lval=1 is_ref=0 refcount=1
第二行,会增加*(zval*)->refount=2
第三行,执行值的拷贝,分开成为一个被引用,一个未被引用.其中被引用的其refcount=2

 
发布了17 篇原创文章 · 获赞 1 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览