PHP不需要显式内存管理,由Zend引擎负责管理
PHP的内存管理,包括:足够内存
可用内存获取部分内存
使用后的内存,是否销毁还是重新分配
Zend内存管理机制:
接口层
一些宏定义 ### 堆层Heap(_zend_mm_heap) 初始化内存,调用zend_mm_startup PHP内存管理维护3个列表: 1. 小块内存列表free_buckets 2. 大块内存列表large_free_buckets 3. 剩余内存列表rest_buckets
2个HashTable结构,难点是查找和计算内存地址
free_buckets
Hash函数为:
#define ZEND_MM_BUCKET_INDEX(true_size) ((true_size>>ZEND_MM_ALIGNMENT_LOG2)-(ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2))
large_free_buckets
Hash函数为:
#define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S)
static inline unsigned int zend_mm_high_bit(size_t _size){
..// 省略若干不同环境的实现
unsignedint n =0;
while(_size !=0) {
_size = _size >>1; n++;}
return n-1;
}
存储层内存分配方式对堆层透明化,实现分离
不同的内存分配方案,有对应的处理函数
PHP底层对内存的管理,就是围绕这小块内存列表、大块内存列表和剩余内存列表来分层进行的
ZendMM向系统进行的内存申请,并不是有需要时向系统即时申请
而是由ZendMM的最底层(heap层)先向系统申请一大块的内存
通过对上面三种列表的填充, 建立一个类似于内存池的管理机制
在程序运行需要使用内存的时候,ZendMM会在内存池中分配相应的内存供使用
这样做的好处是避免了PHP向系统频繁的内存申请操作
ZendMM对内存分配的处理步骤:内存检查
命中缓存,找到内存块,转5
在Heap层中搜索合适大小的内存块,找到后,转5
3中未找到,使用ZEND_MM_STORAGE_ALLOC申请新内存块,转6
使用zend_mm_remove_from_free_list将已经使用的block节点在zend_mm_free_list中删除
内存分配完毕,对zend_mm_heap结构中的各种标志型变量进行维护,包括large_free_buckets、peak、size等
返回分配的内存地址
内存的销毁
当程序unset一个变量或者其他的释放行为时,ZendMM并不会立即将内存交回给系统。而是在自己维护的内存池中将其重新标示为可用
ZendMM将内存块以整理回收到zend_mm_heap的方式,回收到内存池中。程序使用的所有内存,在进程结束时统一交还给系统。
垃圾回收
PHP5.3之前
引用计数方式的内存动态管理
PHP中所有的变量都是以zval变量的形式存在,当变量的引用计数变为0时,PHP将在内存中销毁这个变量。
PHP5.3的垃圾回收
为了打破引用计数中的循环引用,引入垃圾回收机制
struct _zval_struct {
zvalue_value value;
zend_unit refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
}
该垃圾回收算法描述:
首先,PHP会分配一个固定大小的根缓冲区,用于存放固定数量的zval变量。默认10,000
当根缓冲区满时,PHP就会执行垃圾回收。算法如下:对每个根缓冲区中的根zval按照深入优先遍历算法遍历到所有的zval,并将每个zval的refcount减1并且标记,防止多减
再对每个根缓冲区中的根zval深度优先遍历,如果某个zval的refcount不为0,则+1,否则保持不变
清空缓冲区中的所有根,然后销毁所有refcount为0的zval,并回收其内存
####与垃圾回收算法相关的PHP配置
1、可以通过修改php.ini中的zend.enable_gc来打开或关闭PHP的垃圾回收机制,也可以通过调用gc_enable()或gc_disable()打开或关闭PHP的垃圾回收机制。
2、在PHP5.3中即使关闭了垃圾回收机制,PHP仍然会记录可能根到根缓冲区,只是当根缓冲区满额时,PHP不会自动运行垃圾回收
3、当然,任何时候您都可以通过手工调用gc_collect_cycles()函数强制执行内存回收。