`

[转]PHP的内存管理

 
阅读更多

     另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们平时开发扩展, 修复PHP的bug的时候, 却对这一部分的知识需要有一个良好的理解. PHP开发组内的很多朋友也对这块不是很清楚, 所以我觉得有必要专门写一下.

     一些基本的概念, 我就不赘述了, 因为看代码很容易能看懂, 我这里就主要介绍几个看代码没那么容易看懂的点, 为什么这么说呢, 呵呵, 我在写文章之前, 查找了下已有的资料, 已避免重复功, 其中看到了TIPI项目对这部分的描述, 我只能说, 错误很多. 所以, 我想这部分就是看代码也没那么容易看懂的点 :)

     目前, 英文版的介绍也在撰写中: Zend MM

     Zend Memory Manager, 以下简称Zend MM, 是PHP中内存管理的逻辑. 其中有一个关键数据结构: zend_mm_heap: 

     Zend MM把内存非为小块内存和大块内存俩种, 区别对待, 对于小块内存, 这部分是最最常用的, 所以追求高性能. 而对于大块内存, 则追求的是稳妥, 尽量避免内存浪费.

     所以, 对于小块内存, PHP还引入了cache机制: 

     Zend MM 希望通过cache尽量做到, 一次定位就能查找分配.

     而一个不容易看懂的点是free_buckets的申明:

     Q: 为什么free_buckets数组的长度是ZEND_MM_NUMBER_BUCKET个?

     A: 这是因为, PHP在这处使用了一个技巧, 用一个定长的数组来存储ZEND_MM_NUMBER_BUCKET个zend_mm_free_block, 如上图中红色框所示. 对于一个没有被使用的free_buckets的元素, 唯一有用的数据结构就是next_free_block和prev_free_block, 所以, 为了节省内存, PHP并没有分配ZEND_MM_NUMBER_BUCKET * sizeof(zend_mm_free_block)大小的内存, 而只是用了ZEND_MM_NUMBER_BUCKET * (sizeof(*next_free_block) + sizeof(*prev_free_block))大小的内存..

     我们来看ZEND_MM_SMALL_FREE_BUCKET宏的定义:

#define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \\
    (zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \\
        sizeof(zend_mm_free_block*) * 2 - \\
        sizeof(zend_mm_small_free_block))

     之后, Zend MM 保证只会使用prev和next俩个指针, 所以不会造成内存读错..

     那么, 第二个不容易看懂的点, 就是PHP对large_free_buckets的管理, 先介绍分配(TIPI项目组对此部分的描述有些含糊不清):

static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size)

     large_free_buckets可以说是一个建树和双向列表的结合: 

     双向列表保持着同样size的内存块, 而左右孩子(child[0]和child[1])分别代表着键值0和1, 这个键值是指什么呢?

     我们来举个例子, 比如我向PHP申请一个true_size为0b11010大小的内存, 经过一番步骤以后, 没有找到合适的内存, PHP进入了zend_mm_search_large_block的逻辑来在large_free_buckets中寻找合适的内存:

    1. 首先, 计算true_size对应的index, 计算方法是, 获取true_size中最高位1的序号(zend_mm_high_bit), 对应的汇编指令是bsr(此处, TIPI项目错误的说明为: “这个hash函数用来计算size的位数,返回值为size二进码中1的个数-1″).

    2. 然后在一个位图结构中, 判断是否存在一个大于true_size的可用内存已经存在于large_free_buckets, 如果不存在就返回:

size_t bitmap = heap->large_free_bitmap >> index;
if (bitmap == 0) {
   return NULL;
}

    3. 判断, free_buckets[index]是否存在可用的内存:

if (UNEXPECTED((bitmap & 1) != 0))

    4. 如果存在, 则从free_buckets[index]开始, 寻找最合适的内存, 步骤如下:

     1. 从free_buckets[index]开始, 如果free_buckets[index]当前的内存大小和true_size相等, 则寻找结束, 成功返回.

     2. 查看true_size的当前最高位, 如果为1. 则在free_buckets[index]->child[1]下面继续寻找, 如果free_buckets[index]->child[1]不存在, 则跳出. 如果true_size的当前最高位为0, 则在free_buckets[index]->child[0]下面继续寻找, 如果free_buckets[index]->child[0]不存在, 则在free_buckets[index]->child[1]下面寻找最小内存(因为此时可以保证, 在free_buckets[index]->child[1]下面的内存都是大于true_size的)

     3. 出发点变更为2中所述的child, 左移一位ture_size.

    5. 如果上述逻辑并没有找到合适的内存, 则寻找最小的”大块内存”:

   /* Search for smallest "large" free block */
    best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)];
    while ((p = p->child[p->child[0] != NULL])) {
        if (ZEND_MM_FREE_BLOCK_SIZE(p) < ZEND_MM_FREE_BLOCK_SIZE(best_fit)) {
            best_fit = p;
        }
    }

    注意上面的逻辑, (p = p->child[p->child[0] != NULL]), PHP在尽量寻找最小的内存.

    为什么说, large_free_buckets是个键树呢, 从上面的逻辑我们可以看出, PHP把一个size, 按照二进制的01做键, 把内存大小信息反应到了键树上, 方便了快速查找.

    另外, 还有一个rest_buckets, 这个结构是个双向列表, 用来保存一些PHP分配后剩下的内存, 避免无意义的把剩余内存插入free_buckets带来的性能问题(此处, TIPI项目错误的描述为: “这是一个只有两个元素的数组。 而我们常用的插入和查找操作是针对第一个元素,即heap->rest_buckets[0]“).

分享到:
评论

相关推荐

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

    总的来说,深入理解PHP内存管理,编写出高效、无泄漏的代码,是每个PHP开发者必备的技能。这不仅涉及到内存的分配和释放,还包括错误处理策略以及如何避免因程序设计不当而产生的内存问题。只有这样,才能确保长时间...

    关于php内存管理与讨论

    【PHP内存管理详解】 在PHP编程中,内存管理是一个至关重要的环节,特别是对于那些长期运行的服务器程序,如Web服务器和PHP扩展模块。了解PHP如何分配和释放内存能帮助开发者编写更加高效和稳定的代码。 一、内存...

    提高php内存.zip

    以下是一些关于PHP内存管理的核心知识点: 1. **内存限制(memory_limit)**: PHP的`memory_limit`配置项决定了脚本可以使用的最大内存。默认情况下,这个值可能设置为128MB,如`phpstudy-php.ini`中的设置所示。...

    EasyTask简单易用的PHP常驻内存多进程任务管理器.zip

    EasyTask是一款专为PHP设计的轻量级常驻内存多进程任务管理器,它使得在PHP环境中实现复杂的并发处理和任务调度变得简单易行。EasyTask的核心特性是保持任务状态在内存中,避免了传统PHP脚本每次执行时重新加载数据...

    解决PHP里大量数据循环时内存耗尽的方法

    首先,要理解PHP的内存管理机制。PHP脚本默认有内存使用上限,可以通过调整php.ini配置文件中的`memory_limit`参数来改变这一上限。但仅仅增加内存限制并不能根本解决内存溢出的问题,有时可能需要改变程序的处理...

    解析PHP中的内存管理,PHP动态分配和释放内存

    本文将深入探讨PHP内存管理的各个方面。 首先,PHP中的内存管理与C语言有所不同。在PHP中,创建和操作字符串非常简单,如`$str = 'hello world';`,字符串可以被自由修改、拷贝和移动。相比之下,C语言中的静态字符...

    java php python erlang 千万级内存数据性能比较

    尽管PHP在内存管理和性能上可能不如Java或Erlang,但在特定场景下,如快速原型开发和轻量级数据处理,PHP依然表现出色。 Python,以其简洁的语法和强大的科学计算库而备受青睐,"python_dict_turple_test.py"可能...

    PHP7内核剖析,包括php基本框架,变量,Zend虚拟机,php基本语法实现,内存管理,线程安全,扩展开发,命名空间等

    掌握这些内存管理技巧,有助于避免内存泄漏,提升程序的稳定性和效率。 线程安全在多进程或多线程环境中尤为重要。虽然PHP7默认是非线程安全的,但理解其潜在的线程不安全性,以及如何在需要时启用线程安全模式,...

    PHP与C++进行IPC命名管道共享内存通信

    1. 锁管理:由于共享内存可能被多个进程同时访问,因此需要使用互斥锁(mutex)或者信号量(semaphore)来确保数据的一致性。 2. 错误处理:在处理管道和共享内存时,可能出现各种错误,如创建失败、读写错误等,...

    nginx+php-fpm模式php内存泄漏探究1

    这引发了关于PHP-FPM生命周期和内存管理的讨论。 Nginx通过fork出多个子进程(worker)来处理用户请求,而PHP-FPM同样会创建子进程来处理PHP脚本。当Nginx的worker接收到请求时,会将请求传递到一个socket,PHP-FPM...

    PHP简单管理Libvirt虚拟机

    【PHP开发Libvirt虚拟机管理】是一个以PHP编写的轻量级工具,旨在简化Libvirt虚拟机的管理工作。Libvirt是一个强大的开源库,用于管理和控制虚拟化平台,如KVM、Xen、QEMU等。这个PHP管理器利用Libvirt的API来执行...

    php监控linux流量,cpu利用率,磁盘利用率,内存利用率

    在IT管理领域,实时监控系统资源的使用情况是至关重要的,特别是对于运行PHP应用程序的Linux服务器。本项目专注于监控Linux系统的四个关键指标:流量、CPU利用率、磁盘利用率和内存利用率。下面将详细介绍如何实现这...

    [PHP] pmap可以查看进程占用内存的详细情况

    PHP进程内存占用情况查看和分析 在 PHP 中,我们可以使用 pmap 命令来查看进程...pmap 命令是 PHP 开发者和系统管理员的一个有力工具,能够帮助我们了解 PHP 进程的内存占用情况,优化 PHP 应用程序的性能和内存使用。

    PHP常驻内存的多进程任务管理器Composer包

    PHP常驻内存的多进程任务管理器Composer包。以进程管理为出发点,同时也支持为每个进程设置定时执行功能,您可以用它来完成需要重复运行的任务(如订单超时自动取消,短信邮件异步推送,队列/消费者/频道订阅者等等),...

    PHP中文件缓存转内存缓存的方法

    PHP中实现文件缓存转内存缓存的方法可以显著提升数据处理的速度和效率。这种转换的核心在于将原本存储于文件系统中的缓存数据迁移到内存中,由于内存的读写速度远远高于磁盘,因此可以显著减少数据加载和处理时间,...

    计算机后端-PHP视频教程. Memcached12 memcached内存管理机制.wmv

    计算机后端-PHP视频教程. Memcached12 memcached内存管理机制.wmv

    php常驻内存框架 EasyTask

    PHP是一种广泛使用的开源脚本语言,尤其适合Web开发,但在处理持久化任务和内存管理方面相对较弱,因此EasyTask这类框架的出现,为PHP开发者提供了更强大的工具。 **详细知识点** 1. **内存管理**:PHP默认情况下...

    基于PHP的校园信息管理系统的设计与实现

    【基于PHP的校园信息管理系统的设计与实现】是一个典型的IT毕业设计项目,主要涉及PHP编程语言在构建校园管理系统中的应用。PHP是一种广泛使用的开源脚本语言,尤其适合于Web开发,可以嵌入到HTML中,创建动态交互式...

Global site tag (gtag.js) - Google Analytics