深入理解PHP内存管理之谁动了我的内存

2023-11-17

首先让我们看一个问题: 如下代码的输出,

var_dump(memory_get_usage());
$a = "laruence";
var_dump(memory_get_usage());
unset($a);
var_dump(memory_get_usage());


输出(在我的个人电脑上, 可能会因为系统,PHP版本,载入的扩展不同而不同):

int(90440)
int(90640)
int(90472)

注意到 90472-90440=32, 于是就有了各种的结论, 有的人说PHP的unset并不真正释放内存, 有的说, PHP的unset只是在释放大变量(大量字符串, 大数组)的时候才会真正free内存, 更有人说, 在PHP层面讨论内存是没有意义的.


那么, 到底unset会不会释放内存? 这32个字节跑哪里去了?

要回答这个问题, 我将从俩个方面入手:

这32个字节去哪里了

首先我们要打破一个思维: PHP不像C语言那样, 只有你显示的调用内存分配相关API才会有内存的分配. 
也就是说, 在PHP中, 有很多我们看不到的内存分配过程.
比如对于:

$a = "laruence";

隐式的内存分配点就有:

1. 为变量名分配内存, 存入符号表
2. 为变量值分配内存

所以, 不能只看表象.
第二, 别怀疑,PHP的unset确实会释放内存(当然, 还要结合引用和计数, 这部分的内容请参看我之前的文章深入理解PHP原理之变量分离/引用), 但这个释放不是C编程意义上的释放, 不是交回给OS.
对于PHP来说, 它自身提供了一套和C语言对内存分配相似的内存管理API:

emalloc(size_t size);
efree(void *ptr);
ecalloc(size_t nmemb, size_t size);
erealloc(void *ptr, size_t size);
estrdup(const char *s);
estrndup(const char *s, unsigned int length);

这些API和C的API意义对应, 在PHP内部都是通过这些API来管理内存的.

当我们调用emalloc申请内存的时候, PHP并不是简单的向OS要内存, 而是会像OS要一个大块的内存, 然后把其中的一块分配给申请者, 这样当再有逻辑来申请内存的时候, 就不再需要向OS申请内存了, 避免了频繁的系统调用.

比如如下的例子:

<?php
var_dump(memory_get_usage(TRUE)); //注意获取的是real_size
$a = "laruence";
var_dump(memory_get_usage(TRUE));
unset($a);
var_dump(memory_get_usage(TRUE));

输出:

int(262144)
int(262144)
int(262144)

也就是我们在定义变量$a的时候, PHP并没有向系统申请新内存.

同样的, 在我们调用efree释放内存的时候, PHP也不会把内存还给OS, 而会把这块内存, 归入自己维护的空闲内存列表. 而对于小块内存来说, 更可能的是, 把它放到内存缓存列表中去(后记, 某些版本的PHP, 比如我验证过的PHP5.2.4, 5.2.6, 5.2.8, 在调用get_memory_usage()的时候, 不会减去内存缓存列表中的可用内存块大小, 导致看起来, unset以后内存不变, 见评论).

现在让我来回答这32个字节跑哪里去了, 就向我刚才说的, 很多内存分配的过程不是显式的, 看了下面的代码你就明白了:

<?php
var_dump("I am Laruence, From http://www.laruence.com");
var_dump(memory_get_usage());
$a = "laruence";
var_dump(memory_get_usage());
unset($a);
var_dump(memory_get_usage());

输出:

string(43) "I am Laruence, From http://www.laruence.com"
int(90808) //赋值前
int(90976)
int(90808) //是的, 内存正常释放了

90808-90808 = 0, 正常了, 也就是说这32个字节是被输出函数给占用了(严格来说, 是被输出的Header占用了)

只增不减的数组

Hashtable是PHP的核心结构(了解Hashtable, 可以参看我之前的文章深入理解PHP之数组(遍历顺序)), 数组也是用她来表示的, 而符号表也是一种关联数组, 对于如下代码:

var_dump("I am Laruence, From http://www.laruence.com");
var_dump(memory_get_usage());
$array = array_fill(1, 100, "laruence");
foreach ($array as $key => $value) {
    ${$value . $key} = NULL;
}
var_dump(memory_get_usage());
foreach ($array as $key=> $value) {
    unset(${$value . $key});
}
var_dump(memory_get_usage());

我们定义了100个变量, 然后又按个Unset了他们, 来看看输出:

string(43) "I am Laruence, From http://www.laruence.com"
int(93560)
int(118848)
int(104448)

Wow, 怎么少了这么多内存?
这是因为对于Hashtable来说, 定义它的时候, 不可能一次性分配足够多的内存块, 来保存未知个数的元素, 所以PHP会在初始化的时候, 只是分配一小部分内存块给HashTable, 当不够用的时候再RESIZE扩容,

而Hashtable, 只能扩容, 不会减少, 对于上面的例子, 当我们存入100个变量的时候, 符号表不够用了, 做了一次扩容, 而当我们依次unset掉这100个变量以后, 变量占用的内存是释放了(118848 – 104448), 但是符号表并没有缩小, 所以这些少的内存是被符号表本身占去了…

现在, 你是不是对PHP的内存管理有了一个初步的认识了呢?


http://www.laruence.com/2011/03/04/1894.html


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解PHP内存管理之谁动了我的内存 的相关文章

  • PHP - While/Else 错误? [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我有以下
  • Codeigniter查看和回显

    我有一个在 codeigniter 中处理网页侧栏的函数 如下 function process sidebar this gt load gt view first access 1 this gt load gt view second
  • 如何替换每隔一个的空白?

    我想用 替换每个第二个空格 using preg replace 并输入这样的字符串 string a b c d e f g h i 应该会产生如下输出 a b c d e f g h i thanks 您可以组合使用explode ht
  • Laravel 8 图像不显示

    我在用Laravel 8 试图显示来自storage app subject image 路径 但不显示 下面是我的代码blade img src asset subject image data gt subject image alt
  • php同时上传最大文件数

    我正在使用标签 用于使用 php 上传多个文件 我注意到 如果我选择超过 20 个文件 php 只会上传前 20 个文件 有没有办法扩大这个限制 这个限制被添加到PHP 5 2 12 https www php net releases 5
  • PHP如何计算时差? [复制]

    这个问题在这里已经有答案了 我必须计算日期时间差 如何在 PHP 中做到这一点 我需要准确的小时 分钟和秒 有人有这方面的脚本吗 Use the diff 方法 http www php net manual en datetime dif
  • 在 PHP cURL 中设置 FTP 模式

    我有一些代码使用 PHP cURL 库通过 FTP 上传一些文件 我被告知将 FTP 模式从被动切换为主动 但我找不到如何做到这一点 相关文档中似乎没有选项 我实际上不确定它当前使用的是什么模式 或者我如何查看它 这个问题的答案是使用 cu
  • 使用 Laravel 返回一对多 Eloquent 关系中的最后一条记录

    假设存在一个One To Many关系 其中用户有许多工作 以及最后一条记录job表是用户当前的作业 让用户返回上一份工作的更好方法是什么 这是我尝试过的 User Class public function ejob return thi
  • 如何使用 PHP 向用户发送每日电子邮件通知?

    我有一个简单的用户注册表单 其中有一个复选框 如果用户的任何项目有活动 用户可以每天收到电子邮件通知 就像 Stack Overflow 有一个 通知 电子邮件受保护 cdn cgi l email protection每天都有新的答案 我
  • PHP 数组转换为 Javascript 数组

    下午都 下面的代码工作完美 但是 我需要将 php sql 数组的每一行拉出并放入脚本 var 中 关于如何编写可以做到这一点的 while 循环有什么想法吗 谢谢你的帮助 var enableDays enableDays push 附加
  • 如何在 php 和 mongodb 中使用 findAndModify

    我想将 id 加 1 但运行 php 页面时出现问题 错误是 Fatal error Call to undefined method MongoCollection findAndModify in C wamp www 我的代码是
  • PHP 的 Dom Node 查找 href 属性问题

    我正在尝试使用 php domDocument 从某些数据的 url 中提取 href 以下拉动网址的锚点 但我想要网址 events i race 1 trim cols gt item 1 gt nodeValue 如果有帮助的话 这里
  • 如何获取wordpress中文件的本地路径

    由于在wordpress中 上传的文件 图像以3种不同的大小存储 从而占用内存 我有一个代码可以根据给定图像的 URL 来调整图像的大小 调整大小的代码是 img wp get image editor image url if is wp
  • 在 PHP 中扩展单例

    我正在使用一个 Web 应用程序框架 其中一部分由许多服务组成 所有服务均以单例形式实现 它们都扩展了一个 Service 类 其中实现了单例行为 如下所示 class Service protected static instance p
  • 高级自定义字段 - WordPress

    在使用时自定义字段插件 我无法让它返回任何数据 我创建了一个名为的字段组book cover thumbnail其中有一篇文章链接到它 谁能明白为什么下面的代码不起作用 img src 我完全没有收到任何错误 没有空格 确保您 a 使用以下
  • PHP 中的基本 URL

    我有一个两难的困境 它已经困扰我很长一段时间了 我有一个本地测试服务器 其设置如下 127 0 0 1 我的网站在离线模式下如下所示 127 0 0 1 websitename index php 我的网站实时版本如下所示 websiten
  • 会话劫持和 PHP

    让我们只考虑服务器对用户的信任 会话固定 为了避免我使用的固定session regenerate id 仅在身份验证中 login php 会话侧劫持 整个站点的 SSL 加密 我安全吗 阅读 OWASPA3 破坏的身份验证和会话管理 h
  • Laravel 上的图像更新并删除旧图像

    尝试在我的更新控制器中实现更新文章似乎可行 但问题是当我只想更新帖子而不上传图像时 旧的总是会被删除 但这是不应该的 这是我的商店功能 public function store Post post post gt update this
  • 如何编辑 Woocommerce 单一产品元模板中显示的内容?

    我正在为客户做一些工作并使用 wordpress woocommerce 他们要求我将类别移动到我已经完成的单个产品页面上的产品名称下 但他们不希望它打印 类别 类别 1 类别 2 等 他们希望删除 类别 并且它实际上只列出类别的名称 而不
  • 带缓存的简约 PHP 模板引擎,但不带 Smarty?

    有大量的问题 https stackoverflow com search q php template engine cache寻找 正确的 PHP 模板引擎 但它们都不专注于缓存 有谁知道一个轻量级 高质量 基于 PHP 5 的模板引擎

随机推荐

  • File类的知识

    File 文章目录 File 概述 构造方法 抽象路径 成员方法 创建 删除 判断和获取和遍历 判断 获取 遍历 概述 java编写的一个专门用于描述计算机中的文件和文件夹的类 1 是文件和目录路径名的抽象表示 2 文件和目录是可以通过Fi
  • System has not been booted with systemd as init system (PID 1). Can‘t operate

    system has not been booted with systemd as init system pid 1 can t operate Error 报错 System has not been booted with syst
  • SMTP协议解读以及如何使用SMTP协议发送电子邮件

    电子邮件协议中POP3协议用于接收邮件 SMTP协议用于发送邮件 SMTP的全称为Simple Mail Transfer Protocol 也就是简单邮件传输协议 字如其名 相较于POP3而言 SMTP确实比较简单 这里的简单并不是指SM
  • 李承鹏小说

    1 李可乐抗拆记 城镇化进程 拆迁是个大事情 房子是普通老百姓生活中最重要的东西 一个被拆迁的社区就是一个矛盾激化的社会 有人想终于可捞一笔了 一辈子也就这么一次暴富的机会 有人喜欢老地方 不在乎钱 就不要搬 大部分都是乐意搬 只要补偿合理
  • SprongBoot集成MinIo

    SprongBoot集成MinIo 1 集成MinIo 1 1 添加依赖
  • 第八篇 VGGNet——网络实战

    文章目录 摘要 1 项目结构 2 划分训练集和测试集 3 计算mean和Standard 3 1 标准化的作用 3 2 归一化的作用 4 训练 4 1 导入项目使用的库 4 2 设置随机因子
  • 利用ANSYS随机振动分析功能实现随机疲劳分析

    ANSYS随机振动分析功能可以获得结构随机振动响应过程的各种统计参数 如 均值 均方根和平均频率等 根据各种随机疲劳寿命预测理论就可以成功地预测结构的随机疲劳寿命 本文介绍了ANSYS随机振动分析功能 以及利用该功能 按照Steinberg
  • Android SDK Android NDK 官方下载地址

    Android SDK Android NDK 官方下载地址 Android NDK r6b Windows http dl google com android ndk android ndk r6b windows zip Mac OS
  • VisualStudio(2022)- 打包项目文件为.exe安装包

    目录 前言 一 安装扩展 二 制作安装包 setup文件 2 1 添加setup项目 2 2 配置setup项目 2 3 添加项目文件到setup项目中 扩展知识 三个文件夹说明 2 4 设置项目主输出 2 5 设置快捷方式 2 6 生成安
  • 数据结构--单链表的c语言实现(超详细注释/实验报告)

    数据结构 单链表的c语言实现 超详细注释 实验报告 知识小回顾 在顺序表中 用一组地址连续的存储单元来一次存放线性表的结点 因此结点的逻辑顺序和物理顺序是一致的 而链表则不然 链表是用一组任意的存储单元来存放线性表的结点 这组储存单元可以是
  • 面向对象之魔法方法

    目录 概念 魔法方法分类 构造与初始化 new new 的使用场景 init del 类的表示 str repr bool 访问控制 比较操作 eq ne lt gt 容器类操作 重要 可调用对象 序列化 getstate setstate
  • linux initcall机制

    Linux系统启动过程很复杂 因为它既需要支持模块静态加载机制也要支持动态加载机制 模块动态加载机制给系统提供了极大的灵活性 驱动程序既可支持静态编译进内核 也可以支持动态加载机制 Linux系统中对设备和子系统的初始化在最后进行 主要过程
  • 网关、路由、DNS详细解释

    网关 Gateway 又称网间连接器 协议转换器 网关在网络层以上实现网络互连 是最复杂的网络互连设备 仅用于两个高层协议不同的网络互连 网关既可以用于广域网互连 也可以用于局域网互连 针对普通客户来说 网关就是运营商的交换机端口地址 也就
  • AI 代码辅助工具-codeium(免费)---Android studio

    Codeium官网说明 JetBrains IDEs Tutorial Codeium Free AI Code Completion ChatCodeium offers best in class AI code completion
  • Linux 系统中的 SNMP Trap及常用OID

    浅谈 Linux 系统中的 SNMP Trap 转载 http www ibm com developerworks cn linux l cn snmp 本文讲解 SNMP Trap 在介绍 Trap 概念之前 首先认识一下 SNMP 吧
  • Windows 查看端口占用并关闭

    Windows 查看端口占用并关闭 在启动服务的时候 可能会遇到端口被占用的情况 这时就要知道哪个服务占用了这个端口 并将其关闭 然后再启动服务就不会存在端口占用了 这里以 Tomcat 的默认端口 7080 为例 一 打开命令窗口 Win
  • 概率图模型笔记(9-10)——Inference-Belief Propagation

    9 Inference Belief Propagation part1 9 1 Belief Propagation 9 1 1 聚类图 Cluster Graphs 聚类图即这样的一个无向图 节点是团 Ci X1 Xn C i sube
  • element-plus 组件解析 - Collapse 折叠面板

    element plus 组件解析 Collapse 折叠面板 1 组件介绍 2 组件组成 3 组件实现 3 1 el collapse 1 v model activeNames 2 手风琴效果 3 el collapse 关键逻辑 3
  • Vue 源码之Vue 响应式原理【完整版】

    写在前面 由于昨天写的文章 Vue 源码之Vue视图更新原理 一 与今天的内容代码有些相关联 所以开头先进行简单的回顾阐述 也方便对内容进行完整的阅读 Vue 视图更新原理 Vue 的视图更新原理主要涉及的是响应式相关API Object
  • 深入理解PHP内存管理之谁动了我的内存

    作者 Laruence 本文地址 http www laruence com 2011 03 04 1894 html 转载请注明出处 首先让我们看一个问题 如下代码的输出 var dump memory get usage a larue