首先要理解PHP的内存分配是一次性向系统申请开辟的,PHP自身有个内存管理池,每次申请内存都会先在管理池中寻找合适的内存块,找不到才向系统申请内存,因此,脚本运行的时间越长(例如守护进程运行的脚本),PHP占有的内存也就越大,所以及时释放内存很重要(PHP5.3后引入新的垃圾回收机制),释放后的内存不交回给系统,而是放在内存管理池中继续使用;但是,PHP这种机制可以有效避免了频繁向系统申请内存,减少对OS的请求次数(PHP预定义常量变量多)。
PHP的内存管理可以看作为三层:接口层(emalloc、efree),堆层(heap),存储层(storge)。
堆层是PHP内存管理机制的核心,而真正向系统申请内存的是存储层(malloc()、mmap()申请、efree()释放)。堆层通过调用存储层的内存分配方法时,是以大块大块的方式申请内存,存储层的作用是将内存分配的方式对堆层透明化。
PHP底层的内存管理,是围绕三块内存列表进行的,小块内存列表(free_buckets),大块内存列表(large_free_buckets),剩余内存列表(rest_buckets)
PHP在内存申请时,并不是需要时才想系统申请,而是由堆层先向系统申请一大块内存,通过对上述三种列表的填充,建立一个类似内存池的管理机制。在程序运行需要使用内存时,会在内存中分配对用的内存使用,这样做的好处时避免了PHP频繁想系统进行内存申请操作。
当程序unset一个变量或释放内存时,并不会直接将内存交回给系统,而是在自身维护的内存池中将其重新标记为可用状态,按照内存的大小整理到上述三种列表中,以备后续使用。
引用计数( Reference Counting )
php对每个变量维护一个引用计数(refcount),用于追踪有多少个引用指向该变量。创建一个变量时,refcount为1,每当有一个引用指向该变量时,refcount + 1,每当有引用被删除时,refcount - 1。当refcount = 0时,PHP就会释放这块内存。
$a = new stdClass(); // 创建对象,引用计数为 1
$b = $a; // 新引用,引用计数增加到 2
unset($a); // 引用计数减少到 1
unset($b); // 引用计数减少到 0,对象被销毁
循环引用
class Node {
public $next;
}
$a = new Node();
$b = new Node();
$a->next = $b;
$b->next = $a;
unset($a);
unset($b); // 此时两者都不为 0,但已无可达路径
垃圾收集器
PHP在5.3之后引入了新的垃圾回收器( Garbage Collector, GC ),并不直接看refcount是否等于0。
垃圾收集器会定期扫描所有变量和对象,标记所有可访问的数据结构,然后清除未被标记的数据结构,释放内存。
● 标记阶段(Mark Phase): 垃圾收集器从根变量(全局变量、超全局变量、函数内的静态变量等)开始,递归地遍历并标记所有可访问的数据结构,以确保不会被回收。
● 清除阶段(Sweep Phase): 垃圾收集器清除未被标记的数据结构,释放它们占用的内存。
内存管理策略
垃圾收集器会根据内存使用情况自动触发。当PHP进程中的内存分配接近限制时,垃圾收集器会启动清除未使用的内存。
—— 评论区 ——