免费下载案例集|20+数字化领先企业人才培养实践经验 了解详情
写点什么

深入理解 PHP7 unset 真的会释放内存吗?

  • 2019-11-14
  • 本文字数:2971 字

    阅读完需:约 10 分钟

深入理解 PHP7 unset 真的会释放内存吗?

PHP 提供了 unset 用于释放指定的变量,那么它真的会释放内存吗?本文将从这个话题展开讨论。

1 关于 unset 的一些说法

有人说:


  • unset() 并不真正释放内存;

  • unset() 函数只能在变量值占用内存空间超过 256 字节时才会释放内存空间;

  • 只有当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存;

  • unset() 只是在释放大变量(大量字符串, 大数组)的时候才会真正 free 内存。

2 首先认知 unset 真的是函数吗?

验证方法之一

$ php -r "var_dump(function_exists('unset'));"bool(false)
复制代码

验证方法之二

$ php --rf  unsetException: Function unset() does not exist
复制代码


上面提到的两种检验方法,实际上是不严谨的,比如函数不存在时,会出现相同的输出结果。所以我们在使用时,需要开发人员合理判断当前的使用场景。


那么有没有一种准确的 判断呢?答案一定是有的。


一种途径是从 PHP 源码入手:


Zend/zend_language_scanner.l 找到语法规则:


<ST_IN_SCRIPTING>"unset" {    RETURN_TOKEN(T_UNSET);}
复制代码


另一种途径是从 PHP 官网 unset() 获悉:


Note: 因为这是一个语言构造器而不是一个函数,不能被 可变函数 调用。

3 快速了解语言结构与函数的定义和区别

什么是语言结构?

  • PHP 关键词;

  • PHP 标识符;

  • PHP 语言内置的一种语法规则;

什么是函数及包括哪些?

  • 一段(一块)代码的集合,可以做某一件事儿的程序;

  • 函数分为内部(内置)函数、用户自定义函数、可变函数、匿名函数(闭包函数)。


列举几点两者的区别:


4 正确认识 memory_get_usage 函数

PHP 函数原型如下:


memory_get_usage ([ bool $real_usage = false ] ) : int

复制代码


  • 当 $real_usage 为 false 时,返回当前申请的已经使用的内存大小;

  • 当 $real_usage 为 true 时,返回当前申请的的内存大小,包括已使用和未使用内存;


函数实现 C 源码如下:


ZEND_API size_t zend_memory_usage(int real_usage){#if ZEND_MM_STAT    if (real_usage) {        return AG(mm_heap)->real_size;    } else {        size_t usage = AG(mm_heap)->size;        return usage;    }#endif    return 0;}
复制代码


从源码中看出,memory_get_usage() 函数能正常使用需要 ZMM(Zend Memory Manager)支持,如果关闭 ZMM,PHP 内存分配会切换到系统调用 malloc(),由于 PHP 不跟踪非 emalloc() 分配的内存,此函数会无效,将返回默认值。


提供一种临时关闭 PHP ZMM 方法:


$ export USE_ZEND_ALLOC=0
复制代码


ZMM 默认是开启的,全文皆在开启 ZMM 情况下展开讨论。

5 分析 unset 字符串变量例子

PHP 环境信息如下:


$ php -vPHP 7.3.5 (cli) (built: May 27 2019 20:59:34) ( NTS DEBUG )Copyright (c) 1997-2018 The PHP GroupZend Engine v3.3.5, Copyright (c) 1998-2018 Zend Technologies    with Zend OPcache v7.3.5, Copyright (c) 1999-2018, by Zend Technologies
复制代码

例 1

一个 unset() 小字符串变量例子:


<?phpvar_dump(memory_get_usage());$user = 'fanjiapeng';var_dump(memory_get_usage());unset($user);var_dump(memory_get_usage());
复制代码


在 CLI 模式下执行,输出的数字大小取决于你的环境:


$ php small_string_a.phpint(410064)int(410128)int(410128)
复制代码


这里抛出了一个问题,unset() 之后脚本占用内存空间没有减小呢?


如果我们微调下当前例子,调用 memory_get_usage(true) 函数测试,得到如下输出结果:


int(2097152)int(2097152)int(2097152)
复制代码


Why? 这是因为 PHP 采用的是预分配内存策略,在定义一个变量 $user 时,并没有实时去系统申请内存。

了解 $user 变量构成

<?php$user = 'fanjiapeng';
复制代码


  1. 分配 变量名 内存空间,存入符号表

  2. 分配 变量值 内存空间

  3. 在 ZEND_RETURN 阶段,变量名与变量值关联


一个 PHP 变量由两部分组成:变量名 和 变量值。它们的内存大小分配由 ZMM 负责管理。ZMM 是基于 C 的内存函数库做了一层封装,使得 PHP 开发者不用去操心内存管理上的这些事,只需要专注于业务开发就可以啦,简直爽歪歪。


ZMM 是在 php_module_startup 阶段,向系统一次性申请了一大块内存(2MB)。当有新的变量申请内存时,ZMM 直接在余下的内存池中选择合适的大小。当池子不够使用时,再向系统申请新的内存。


关于 ZMM 介绍在这里就不再展开了哟。

unset 究竟做了哪些事情?

  1. 把 变量值 标记为 删除

  2. 有引用计数的进行相关的处理机制(比如:释放变量值占用的内存)


这个例子中的变量值其实是一个内部(常量)字符串,存储在 interned_strings 哈希表 中。它不需要通过引用计数机制来管理,unset() 也不会去释放它。既然变量不会被释放,那么也就不会存在有回收。依据 memory_get_usage() 函数说明,所以我们才会看到, unset() 之后内存占用大小无变化。


那么内部字符串(interned_strings)是在什么时候释放呢?


关闭 Opcache 时(NTS):



开启 Opcache 时(NTS):


例 2

来看另一个例子,unset() 之后内存占用发生了变化:


<?phpvar_dump(memory_get_usage());$user = 'fanjiapeng' . time();var_dump(memory_get_usage());unset($user);var_dump(memory_get_usage());
复制代码


在 CLI 模式下执行:


$ php small_string_b.phpint(410208)int(410352)int(410272)
复制代码


如果微调一下代码,得到的结果与第一个例子是相同的:


*// var_dump(memory_get_usage(true));*int(2097152)int(2097152)int(2097152)
复制代码


但是第二个例子中的变量值是临时字符串(IS_TMP_VAR),zval 关键信息如下:


(zval).u1.v.type_flags == 1(zval).value.counted.gc.refcount == 1
复制代码


若是这类变量,unset() 直接就释放掉了这部分内存,脚本的实际内存占用值会被减少。由于当前变量值占用内存小于 3072B,属于 small 内存管辖范围,被释放的这部分内存会归还到空闲的内存列表中(ZMM),不会交还给系统。


若 refcount 大于 1,则引用计数减 1,然后进入 PHP 垃圾收集器处理机制。

例 3

再来看一个 unset() 大字符串变量例子:


<?phpvar_dump(memory_get_usage(true));$user = file_get_contents('/tmp/big_string.log'); *// 7845566 B*var_dump(memory_get_usage(true));unset($user);var_dump(memory_get_usage(true));
复制代码


在 CLI 模式下执行:


$ php huge_memory.phpint(2097152)int(9945088)int(2097152)
复制代码


第三个例子中的变量值是临时字符串(IS_VAR),zval 关键信息同上,它们的释放机制也是同理的。


由于当前申请的内存大于 2044 KB,属于 huge 内存管辖范围。由 zend_mm_huge_list 大内存链表结构来管理,是通过 PHP zend_mm_alloc_huge() 函数申请 size 大小内存, 最终调用 Linux mmap() 函数来向操作系统申请内存。


unset() 最终调用 Linux munmap() 函数解除内存映射关系,同时 AG(mm_heap)>real_size 和 AG(mm_heap)->size 减去相应的 size 大小,所以我们能看到脚本占用内 存发生了变化。

6 unset 总结

本文其实用了较大的篇幅讲了 PHP 的内存管理,下面回归正题:unset() 究竟会不会释放内存的问题。


笔者分阶段进行了总结:


若开启 ZMM & 达到释放条件时:


  • unset() 释放小、中变量(small、large),不同于 C/C++ 语言层面上的 free() 内存释放。只会把内存归还给 ZMM,不会交还给系统(OS);

  • unset() 释放大变量(huge),直接释放掉这部分内存;


若关闭 ZMM 时:


  • PHP 内存分配会切换到系统调用 malloc() / free();

  • unset() 会直接与系统内存交互,内存利用率低效。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/XIuto7yzBwr7cCiws_kFUw


2019-11-14 15:402486

评论

发布
暂无评论
发现更多内容

Databend 开源周报第 115 期

Databend

OmniGraffle Pro for mac(专业图表绘制软件) v7.22.1永久激活版

mac

苹果mac Windows软件 OmniGraffle Pro 专业绘图软件

如何将模型原点设置到模型的中心

3D建模设计

模型原点设置 原点归零 原点坐标

如何优化模型渲染性能

3D建模设计

性能提升 渲染优化

专注二次元的 Niji 模型上线独立 APP;华为整体销量已超越苹果丨 RTE 开发者日报 Vol.68

声网

7天狂揽 1.3w star 的 MetaGPT,他们的目标让软件公司为之一惊

鼎道智联

GPT

OpenHarmony页面级UI状态存储:LocalStorage

OpenHarmony开发者

OpenHarmony

万字长文详解低时延股票交易系统的设计

tyrande

交易所 事件溯源 股票系统 低延时

大模型训练中的Delta Tuning优化方法

百度开发者中心

人工智能 深度学习 大模型训练

Mac电脑网页视频处理工具iTubeGo中文免激活版

mac大玩家j

视频处理工具 视频编辑器 在线视频工具

当 BACnet 遇上 IoT,你将体验到不一样的大楼

华为云开发者联盟

云计算 后端 物联网 华为云 华为云开发者联盟

Java时间处理---Java8中时区相关类库介绍

程序员万金游

Java 面试 #java #程序员 #JavaSE

深耕全面预算管理 拥抱企业数字未来

智达方通

自动化 全面预算管理 全面预算管理系统

XMind mac (XMind思维导图)v23.09中文激活版

mac

XMind 思维导图软件 苹果mac Windows软件

「我在淘天做技术」一篇文章告诉你商品团队在做哪些有意思的事?

阿里技术

校园招聘 商品域

Sync Folders Pro for Mac(文件夹数据同步工具) v4.6.7永久激活版

mac

苹果mac Windows软件 Sync Folders Pro 文件夹同步工具

程序员常用的19款办公软件和开发工具推荐!

彭宏豪95

效率工具 开发工具 科技 办公软件 软件开发工具

Java基础面试题【Spring】一

派大星

Java 面试题

大模型训练对深度学习图片检测速度和精度的影响

百度开发者中心

深度学习 大模型训练 千帆大模型平台

大模型训练:数据与算法的关键融合

百度开发者中心

人工智能 模型训练 大模型

Kafka架构详解

tyrande

kafka 系统设计 架构设计 消息列队

HTX 与 Zebec  Protocol 展开深度合作,并将以质押者的身份参与 ZBC Staking

西柚子

HarmonyOS音视频开发概述

HarmonyOS开发者

HarmonyOS

HTX 与 Zebec  Protocol 展开深度合作,并将以质押者的身份参与 ZBC Staking

大瞿科技

iPaaS混合集成平台,打造数字化生态

RestCloud

数据同步 数字化升级 ipaas

YouTube for mac(YouTube客户端)

iMac小白

YouTube客户端 YouTube mac

精彩再现!LLUG 深圳场成功举办 | 附活动资料下载

OpenAnolis小助手

开源 演讲 龙蜥社区 LLUG Linux中国

腾讯云大数据ES:使用Elastic APM监控SpringBoot服务的最佳实践

腾讯云大数据

ES

高效利用内存资源:掌握Redis内存管理与淘汰策略

树上有只程序猿

redis 内存管理

大模型训练:自定义深度学习模型的策略与实践

百度开发者中心

大模型训练 文心千帆

深入理解 PHP7 unset 真的会释放内存吗?_文化 & 方法_范家鹏_InfoQ精选文章