深入探讨PHP中的内存管理问题(2)

  四、 Zend内存管理器

  在上面的"跳出"请求期间解决内存泄漏的方案之一是:使用Zend内存管理(ZendMM)层。引擎的这一部分非常类似于操作系统的内存管理行为-分配内存给调用程序。区别在于,它处于进程空间中非常低的位置而且是"请求感知"的;这样以来,当一个请求结束时,它能够执行与OS在一个进程终止时相同的行为。也就是说,它会隐式地释放所有的为该请求所占用的内存。图1展示了ZendMM与OS以及PHP进程之间的关系。

深入探讨PHP中的内存管理问题


图1.Zend内存管理器代替系统调用来实现针对每一种请求的内存分配。


  除了提供隐式内存清除功能之外,ZendMM还能够根据php.ini中memory_limit的设置控制每一种内存请求的用法。如果一个脚本试图请求比系统中可用内存更多的内存,或大于它每次应该请求的最大量,那么,ZendMM将自动地发出一个E_ERROR消息并且启动相应的"跳出"进程。这种方法的一个额外优点在于,大多数内存分配调用的返回值并不需要检查,因为如果失败的话将会导致立即跳转到引擎的退出部分。

  把PHP内部代码和OS的实际的内存管理层"钩"在一起的原理并不复杂:所有内部分配的内存都要使用一组特定的可选函数实现。例如,PHP代码不是使用malloc(16)来分配一个16字节内存块而是使用了emalloc(16)。除了实现实际的内存分配任务外,ZendMM还会使用相应的绑定请求类型来标志该内存块;这样以来,当一个请求"跳出"时,ZendMM可以隐式地释放它。

  经常情况下,内存一般都需要被分配比单个请求持续时间更长的一段时间。这种类型的分配(因其在一次请求结束之后仍然存在而被称为"永久性分配"),可以使用传统型内存分配器来实现,因为这些分配并不会添加ZendMM使用的那些额外的相应于每种请求的信息。然而有时,直到运行时刻才会确定是否一个特定的分配需要永久性分配,因此ZendMM导出了一组帮助宏,其行为类似于其它的内存分配函数,但是使用最后一个额外参数来指示是否为永久性分配。

  如果你确实想实现一个永久性分配,那么这个参数应该被设置为1;在这种情况下,请求是通过传统型malloc()分配器家族进行传递的。然而,如果运行时刻逻辑认为这个块不需要永久性分配;那么,这个参数可以被设置为零,并且调用将会被调整到针对每种请求的内存分配器函数。

  例如,pemalloc(buffer_len,1)将映射到malloc(buffer_len),而pemalloc(buffer_len,0)将被使用下列语句映射到emalloc(buffer_len):

#define in Zend/zend_alloc.h:
#define pemalloc(size, persistent) ((persistent)?malloc(size): emalloc(size))


  所有这些在ZendMM中提供的分配器函数都能够从下表中找到其更传统的对应实现。

  表格1展示了ZendMM支持下的每一个分配器函数以及它们的e/pe对应实现:

  表格1.传统型相对于PHP特定的分配器。

分配器函数   e/pe对应实现  
void *malloc(size_t count);   void *emalloc(size_t count);void *pemalloc(size_t count,char persistent);  
void *calloc(size_t count);   void *ecalloc(size_t count);void *pecalloc(size_t count,char persistent);  
void *realloc(void *ptr,size_t count);   void *erealloc(void *ptr,size_t count);
void *perealloc(void *ptr,size_t count,char persistent);
 
void *strdup(void *ptr);   void *estrdup(void *ptr);void *pestrdup(void *ptr,char persistent);  
void free(void *ptr);   void efree(void *ptr);
void pefree(void *ptr,char persistent);
 

  你可能会注意到,即使是pefree()函数也要求使用永久性标志。这是因为在调用pefree()时,它实际上并不知道是否ptr是一种永久性分配。针对一个非永久性分配调用free()能够导致双倍的空间释放,而针对一种永久性分配调用efree()有可能会导致一个段错误,因为内存管理器会试图查找并不存在的管理信息。因此,你的代码需要记住它分配的数据结构是否是永久性的。

  除了分配器函数核心部分外,还存在其它一些非常方便的ZendMM特定的函数,例如:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/4fb3e5298dc88da2659c4087ee789511.html