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
Extending.and.Embedding.PHP读书笔记(5)-内存管理
最新推荐文章于 2021-03-28 10:51:00 发布