PHP内核--探究内存管理与缓存机制

前言:

PHP在运行时所需的内存,是一次性向操作系统申请开辟的,而不是分多次。那他是怎么样一次性申请呢,机制又是如何?请看下边介绍。

heap层是PHP内存管理的核心实现,PHP底层对内存的管理, ZendMM向系统进行的内存申请,并不是有需要时向系统即时申请, 而是由ZendMM的最底层(heap层)先向系统申请一大块的内存, 建立一个类似于内存池的管理机制,unset后,ZendMM并不会直接立刻将内存交回给系统,而是只在自身维护的内存池(storge层)中将其重新标识为可用,。
优点:
1.预定义常量变量多,对内存的请求有数百次,避免了PHP向系统频繁的内存申请操作,减少了对OS的请求次数。
2.运行速度会更快,缺点是随着程序的运行时间的变长,内存使用越来越多,所以5.3引入新垃圾回收机制。

详细分析如下:


PHP的内存管理可以被看作是分层(hierarchical)的。 它分为三层:存储层(storage)、堆层(heap)和接口层(emalloc/efree)。 存储层通过 malloc()、mmap() 等函数向系统真正的申请内存,并通过 free() 函数释放所申请的内存。 


存储层通常申请的内存块都比较大,这里申请的内存大并不是指storage层结构所需要的内存大, 只是堆层通过调用存储层的分配方法时,其以大块大块的方式申请的内存,存储层的作用是将内存分配的方式对堆层透明化。
如图下所示,PHP内存管理器。PHP在存储层共有4种内存分配方案: malloc,win32,mmap_anon,mmap_zero, 默认使用malloc分配内存,如果设置了ZEND_WIN32宏,则为windows版本,调用HeapAlloc分配内存, 剩下两种内存方案为匿名内存映射,并且PHP的内存方案可以通过设置环境变量来修改。

图6.1 PHP内存管理器

一.内存的申请
heap层是PHP内存管理的核心实现,PHP底层对内存的管理, 围绕着小块内存列表(free_buckets)、 大块内存列表(large_free_buckets)和 剩余内存列表(rest_buckets)三个列表来分层进行的。ZendMM向系统进行的内存申请,并不是有需要时向系统即时申请, 而是由ZendMM的最底层(heap层)先向系统申请一大块的内存,通过对上面三种列表的填充, 建立一个类似于内存池的管理机制。

在程序运行需要使用内存的时候,ZendMM会在内存池中分配相应的内存供使用。这样做的好处是避免了PHP向系统频繁的内存申请操作,如下面的代码:
  1. <?php  
  2.   $tipi = "o_o\n";  
  3.   echo $tipi;  
  4. ?>  
这是一个简单的php程序,但通过对emalloc的调用计数,只是PHP程序,只赋值了一个变量而已,但是却发现对内存的请求有数百次之多, 当然这非常容易解释,因为PHP脚本的执行,需要大量的环境变量以及内部变量的定义(详细见PHP内核--生命周期), 这些定义本身都是需要在内存中进行存储的。
在编写PHP的扩展时,推荐使用emalloc(申请的是zend_mm_storage层的内存块)来代替malloc(申请的是操作系统的内存块),其实也就是使用PHP的ZendMM来代替 手动直接调用系统级的内存管理。

ZendMM使用_zend_mm_alloc_int函数进行内存分配,流程如下:


从上面的分配可以看出,PHP对内存的分配,是结合PHP的用途来设计的,PHP一般用于web应用程序的数据支持, 单个脚本的运行周期一般比较短(最多达到秒级),内存大块整块的申请,自主进行小块的分配, 没有进行比较复杂的不相临地址的空闲内存合并,而是集中再次向系统请求。 这样做的好处就是运行速度会更快,缺点是随着程序的运行时间的变长, 内存的使用情况会“越来越多”(PHP5.2及更早版本)。 所以PHP5.3之前的版本并不适合做为守护进程长期运行。 (当然,可以有其他方法解决,而且在PHP5.3中引入了新的GC机制,详见后边小节PHP内核--内存泄漏与新垃圾回收机制

二.内存的销毁
ZendMM在内存销毁的处理上采用与内存申请相同的策略,当程序unset一个变量或者是其他的释放行为时,ZendMM并不会直接立刻将内存交回给系统,而是只在自身维护的内存池(storge层)中将其重新标识为可用, 按照内存的大小整理到上面所说的三种列表(small,large,free)之中,以备下次内存申请时使用。
内存销毁的最终实现函数是_efree。在_efree中,内存的销毁首先要进行是否放回cache的判断。 如果内存的大小满足ZEND_MM_SMALL_SIZE并且cache还没有超过系统设置的ZEND_MM_CACHE_SIZE, 那么,当前内存块zend_mm_block就会被放回mm_heap->cache中。 
在内存的销毁过程中,还涉及到引用计数和垃圾回收(GC),将在后边小节进行讨论。参见PHP内核--内存泄漏与新垃圾回收机制

三.缓存
维基百科中有这样一段描述: 凡是位于速度相差较大的两种硬件之间的,用于协调两者数据传输速度差异的结构,均可称之为Cache。 从最初始的处理器与内存间的Cache开始,都是为了让数据访问的速度适应CPU的处理速度, 其基于的原理是内存中“程序执行与数据访问的局域性行为”。 同样PHP内存管理中的缓存也是基于“程序执行与数据访问的局域性行为”的原理。 引入缓存,就是为了减少小块内存块的查询次数(查询前先看是否能命中缓存),为最近访问的数据提供更快的访问方式。
PHP将缓存添加到内存管理机制中做了如下一些操作:
·标识缓存和缓存的大小限制,即何时使用缓存,在某些情况下可以以最少的修改禁用掉缓存
·缓存的存储结构,即缓存的存放位置、结构和存放的逻辑
·初始化缓存
·获取缓存中内容
·写入缓存
释放缓存或者清空缓存列表
缓存本身也是存储在storage层申请的内存中的,如果内存都不够用了,那就得释放缓存啦。
当堆的内存溢出时,程序会调用zend_mm_free_cache释放缓存中。整个释放的过程是一个遍历数组, 对于每个数组的元素程序都遍历其所在链表中在自己之前的元素,执行合并内存操作,减少堆结构中缓存计量数字
阅读更多
个人分类: php
上一篇如何理解php中命名空间概念
下一篇详谈PHP垃圾回收机制
想对作者说点什么? 我来说一句

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

关闭
关闭