PHP内核的存储机制(分离/改变)

【php7内核】数组的实现        数组是PHP中非常强大、灵活的一种数据类型,它的底层实现为散列表(HashTable,也称作:哈希表),除了我们熟悉的PHP用户空间的Array类型之外,内核中也随处用到散列表,比如函数、类、常量、已include文件的索引表、全局符号表等都用的HashTable存储。散列表是根据关键码值(Key value)而直接进行访问的数据结构,它的key - value之间存在一个映射函数,... 阅读详情

前言:
大部分程序员看博客可能不是太喜欢看汉字比较多的文章哈,但本文确实介绍以汉字为主描述,耐心看完,对大部分人来说肯定会有收获!

或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言),在申明一个变量的时候,并不需要显示指明它保存的数据的类型。比如:$a = 1; (整形) $a ="1";(字符串)
一直使用PHP,但它究竟什么,底层是怎么实现才成就了PHP这样方便快捷的弱类型语言。 

最近也查阅了很多书籍,还有相关博客资料,了解到了许多关于PHP内核的一些机制。

php简单的理解就是一个c语言的类库,你去php.net 下面下载一下它的源代码就会发现,首先php的内核是zend engine ,它是一个用c语言写的函数库,用于处理底层的函数管理,内存管理,类管理,和变量管理。在内核上面,他们写了很多扩展,这些扩展大多数都是独立的。用操作系统来比喻的话,zend engine 就是一个操作系统,然后官方提供了很多“应用程序”,只是这个“应用程序” 不是media play 而是 mysql , libxml,dom。当然,你也可以根据zend engine 的api 开发自己的扩展。


下边开始介绍下PHP变量在内核中的存储机制。

PHP是若类型语言,也就是说一个PHP变量可以保存任何的数据类型。但PHP是使用C语言编写的,而C语言是强类型语言是吧,每个变量都会有固定的类型,(一颗通过强类型转变,不过有可能出现问题),那在Zend引擎中如何做到一个变量保存任何数据类型?下边请看它存储结构体。
打开Zend/zend.h头文件,会发现下列结构体Zval:
1.zval结构


 typedef struct _zval_struct zval;
 typedef union _zvalue_value {
    long lval;      /* long value */
    double dval;    /* double value */
    struct {
    char *val; //4字节
    int len;   //4字节
    } str;
    HashTable *ht;    /* hash table value */
    zend_object_value obj;
 } zvalue_value;

 struct _zval_struct {
    /* Variable information */
    zvalue_value value;  /* 变量值保存在这里 12字节*/
    zend_uint refcount;//4字节,变量引用计数器
    zend_uchar type;   /* active type变量类型 1字节*/
    zend_uchar is_ref;//是否变量被&引用,0表示非引用,1表示引用,1字节
    };

2.zend_uchar type
PHP中的变量包括四种标量类型(bool,int,float,string),两种复合类型(array, object)和两种特殊的类型(resource 和NULL)。在zend内部,这些类型对应于下面的宏(代码位置 phpsrc/Zend/zend.h) Zend根据type值来决定访问value的哪个成员,可用值如下:

3.zend_uint refcount__gc  


该值实际上是一个计数器,用来保存有多少变量(或者符号,symbols,所有的符号都存在符号表(symble table)中, 不同的作用域使用不同的符号表,关于这一点,我们之后会论述)指向该zval。在变量生成时,其refcount=1,典型的赋值操作如$a = $b会令zval的refcount加1,而unset操作会相应的减1。在PHP5.3之前,使用引用计数的机制来实现GC,如果一个zval的refcount较少到0,那么Zend引擎会认为没有任何变量指向该zval,因此会释放该zval所占的内存空间。但,事情有时并不会那么简单。后面我们会看到,单纯的引用计数机制无法GC掉循环引用的zval(详见后举例3),即使指向该zval的变量已经被unset,从而导致了内存泄露(Memory Leak)。


4.is_ref__gc
.
这个字段用于标记变量是否是引用变量。对于普通的变量,该值为0,而对于引用型的变量,该值为1。这个变量会影响zval的共享、分离等。关于这点,我们之后会有论述。
正如名字所示,ref_count__gc和is_ref__gc是PHP的GC机制所需的很重要的两个字段,这两个字段的值,可以通过xdebug等调试工具查看。

下面我们围绕zval,展开叙述,PHP变量到底是怎么个存储机制。

Xdebug的安装我在前边PHPstorm Xdebug调试也介绍过,这里不赘述,请看: phpstorm+Xdebug断点调试PHP

安装成功后,你的脚本中,可以通过xdebug_debug_zval打印Zval的信息,用法:


 $var = 1;
 debug_zval_dump($var);
 $var_dup = $var;
 debug_zval_dump($var);

实例一:
    $a = 1;
    $b = $a;
    $c = $b;
    $d = &$c; // 在一堆非引用赋值中,插入一个引用


  整个过程图示如下:

---------------------------------------------------------

实例二:

   $a = 1;
    $b = &$a;
    $c = &$b;
    $d = $c; // 在一堆引用赋值中,插入一个非引用


  整个过程图示如下:


通过实例一、二,展现了,这就是PHP的copy on write写时分离机制、change on write写时改变机制

过程:

PHP在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1,PHP就会执行一个分离的例程, 

     对于上面的实例一代码,当执行到第四行的时候,PHP发现$c指向的zval的refcount大于1,那么PHP就会复制一个新的zval出来,将原zval的refcount减1,并修改symbol_table,使得$a,$b和$c分离(Separation)。这个机制就是所谓的copy on write(写时复制/写时分离)。把$d指向的新zval的is_ref的值 == 1 ,这个机制叫做change on write(写时改变)


结论:
分离指的是:分离两个变量存储的zval的位置,让分开不指向同一个空间! (那如何判定是否要分离呢,依据是什么?见下边)

改变指的是,有&引用赋值时,要把新开辟的zval 的 is_ref 赋值为1


判定是否分离的条件:如果is_ref =1 或recount == 1,则不分离
if((*val)->is_ref || (*val)->refcount<2){
          //不执行Separation
        ... ;//process
  }


---------------------------------------------------------------------------------------------------

实例三:(内存是如何泄漏的)
数组变量与普通变量生成的zval非常类似,但也有很大不同
举例:

$a = $array('one');  
$a[] = &$a;  
xdebug_debug_zval('a'); 

debug_zval_dump打印出zval的结构是:
a: (refcount=2, is_ref=1)=array (
    0 => (refcount=1, is_ref=0)='one', 
    1 => (refcount=2, is_ref=1)=...
)

上述输出中,…表示指向原始数组,因而这是一个循环的引用。如下图所示:


现在,我们对$a执行unset操作,这会在symbol table中删除相应的symbol,同时,zval的refcount减1(之前为2),也就是说,现在的zval应该是这样的结构:
unset($a);
(refcount=1, is_ref=1)=array (
    0 => (refcount=1, is_ref=0)='one', 
    1 => (refcount=1, is_ref=1)=...
)

(应该ref_count=1)

(unset,其实就是打断$a在 符号表(symble table) 与zval 的一个指针映射关系。)

这时,不幸的事情发生了!

  Unset之后,虽然没有变量指向该zval,但是该zval却不能被GC(指PHP5.3之前的单纯引用计数机制的GC)清理掉,$a 被释放,但是$a里的$a[1]也指向了该zval,它没有被释放,导致zval的refcount均大于0。这样,这些zval实际上会一直存在内存中,直到请求结束(参考SAPI的生命周期)。在此之前,这些zval占据的内存不能被使用,便白白浪费了,换句话说,无法释放的内存导致了内存泄露。

  如果这种内存泄露仅仅发生了一次或者少数几次,倒也还好,但如果是成千上万次的内存泄露,便是很大的问题了。尤其在长时间运行的脚本中(例如守护程序,一直在后台执行不会中断),由于无法回收内存,最终会导致系统“再无内存可用”,所以说,一定要避免这种操作。


垃圾回收机制:

1.php原来是通过引用计数器来实现内存回收,也就是是多个php变量可能会引用同一份内存,这种情况unset掉其中一个是不会释放内存的;
    例如:$a = 1; $b = $a; unset($a);//$a开辟的内存不会回收
2.离开了变量的作用域后变量所占用的内存就会被自动清理(不包含静态变量,静态变量在脚本加载时创建,在脚本结束时释放),
    如函数或方法内的局部变量,对这些局部变量进行unset在函数外来看内存也是没有减少的。
3.引用计数有个缺陷,就是当循环引用出现时,计数器没法清0,内存占用会持续到页面访问结束。
    对于这个问题PHP5.3中增加了垃圾回收机制。具体可以查阅文档:http://php.net/manual/zh/features.gc.php
    垃圾回收机制就是最早在Lisp中被提出,关于更多垃圾回收的信息.
    参见维基百科:https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)

本文参考文献:
鸟哥的深入变量引用/分离  http://www.laruence.com/2008/09/19/520.html
 

PHP内核中的变量改变时的处理过程 当变量被赋予一个新值时,PHP内核会根据变量的类型和大小来分配内存。如果变量是一个复杂的数据结构(如数组或对象),PHP内核会分配额外的内存来存储该结构的元数据和成员变量。当在PHP中改变一个变量的值时,PHP内核会进行变量赋值、内存管理、引用计数和垃圾回收等操作。当变量被赋予一个新值或被销毁时,PHP内核会检查变量的引用计数。如果引用计数为零,说明没有任何代码引用该变量,PHP内核将释放该变量所占用的内存。如果变量的引用计数变为零,说明没有任何代码引用该变量,PHP内核会释放该变量所占用的内存。 阅读详情

相关推荐

融合ZILN损失与深度学习的LTV预测:从概率分布到精准营销决策

本文探讨了融合ZILN损失与深度学习的LTV预测方法,通过深度概率模型精准预测用户生命周期价值(LTV),优化营销决策。文章详细解析了ZILN损失函数的优势、模型架构设计要点及工程化落地实践,帮助企业在预算分配、用户分层等场景实现数据驱动的精准营销。

weixin_29093169的博客 308

PHP——底层运行机制与原理

PHP用起来感觉很简单方便,但是要精通PHP,熟悉底层的工作原理也不是一件容易的事。1 PHP的设计理念及特点 多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响,当然,时代发展,PHP也早已支持多线程模型。 弱类型语言:和C/C++、Java、C#等语言不同,PHP是一门弱类型语言。一个变量的类型并不是一开始就确定不变,运行中才会确定并可能发生隐式

Fortune favors the dream of attention. 53万+

<工控><PLC>汇川Easy521系列PLC与汇川SV630N伺服进行EtherCat通讯的相关配置及指令编写

本系列是关于PLC相关的博文,包括PLC编程、PLC与上位机通讯、PLC与下位驱动、仪器仪表等通讯、PLC指令解析等相关内容。PLC品牌包括但不限于西门子、三菱等国外品牌,汇川、信捷等国内品牌。除了PLC为主要内容外,PLC相关元器件如触摸屏(HMI)、交换机等工控产品,如果有值得记录的内容,也会添加进来。

用沸腾的热血,支付我们的人生吧! 2万+

PHP内核--内存泄漏与新垃圾回收机制

垃圾回收机制是一种动态存储分配方案。它会自动释放程序不再需要的已分配的内存块。 自动回收内存的过程叫垃圾收集。垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。 PHP也在语言层实现了内存的动态管理,这在前面的章节中已经有了详细的说明, 内存的动态管理将开发人员从繁琐的内存管理中解救出来。与此配套,PHP也提供了语言层的垃圾回收机制, 让程序员不必过分关心程序

hann的专栏 2509

转录组数据去批次方法整理(combat,combat-seq,removeBatchEffect)

正是因为这些非生物学的因素存在就有可能会导致我们的结果偏离真实的情况,那么实际分析的过程中研究者应当评估是否存在批次效应,并决定是否要进行去批次处理。批次效应是指样本在不同批次处理及测量的情况下引入与生物学情况不相关的技术误差,比如不同试剂耗材,不同操作者,不同的实验时间等。combat函数是基于贝叶斯框架下的调整数据批次效应的函数,主要适用于已经被过滤和标准化后的数据(combat-seq函数是基于二项回归分布下的调整数据批次效应的函数,主要适用于。值得注意的是,即使使用了所谓的去批次效应的工具,

zfyyzhys的博客 7862

AI:Coze、Dify、n8n:三款低代码工具如何选?

Coze、Dify、n8n:三款低代码工具如何选?

Kant2048的博客 2806

php写时复制计数

写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改。 COW最早应用在*nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C++的STL等。 在PHP内核中,COW也是主要的内存优化手段。 在前面关于变量和内存的讨论中,引用计数对变量的销毁与回收中起着至关重要的标识作用。 引用计数存在的意义,就是为了使得COW可以正常运作

企鹅漫步 1268

UE5网络同步优化:GetLifetimeReplicatedProps高级应用指南

本文深入探讨了UE5中GetLifetimeReplicatedProps函数的高级应用与优化策略。文章超越了基础变量注册,重点阐述了如何通过精细的复制条件控制、频率管理及分层复制机制,构建高效的数据同步通道,从而优化网络带宽与游戏性能,是提升多人游戏体验的实用指南。

weixin_29007243的博客 533

php内存机制

Php内存管理机制 PHP不需要显式的对内存进行管理,这些工作都由PHP解释器进行了。由此PHP内部有一个内存管理体系, 它会自动将不再使用的内存垃圾进行释放,php的默认内存大小是32M,在php.ini中memory_limit = 32M memory_get_usage(),这个函数的作用是获取 目前PHP脚本所用的内存大小。 memory_get_peak_usage(),这个函

yun914的博客 1637

从Overleaf上导出Latex论文发布到arxiv详细教程(常见问题与解决)

如果是做cv的论文请选择“Computer Vision and Pattern Recognition”,选择“Artificial Intelligence”可能会被on hold,貌似这个指的是传统的人工智能?我用公司邮箱注册发布一篇论文,一个多月还是“on hold”状态,换成edu邮箱第二天就被发布了。3、有可能是subject class选错了,编辑会帮你手动选择,然后发一封邮件告知你解除……报错缺失bbl等文件,这是因为从Menu中下载的话,是没有bbl文件的。论文被on hold。

沉迷单车的追风少年 3583

Docker安装并运行AnythingLLM的详细教程

通过本文的介绍,你已经掌握了如何在 CentOS 系统上使用 Docker 安装并运行 AnythingLLM。AnythingLLM 提供了强大的功能,支持本地知识库搭建和多模态输入,非常适合个人和企业使用。希望本文能帮助你快速上手并搭建自己的 AI 知识库。

加入“Super Entity”,与全能开发团队共探AI智能体与数字人项目,开启前沿技术之旅。 5837

PHP内核的存储机制(分离/改变)【转载】

前言: 大部分程序员看博客可能不是太喜欢看汉字比较多的文章哈,但本文确实介绍以汉字为主描述,耐心看完,对大部分人来说肯定会有收获! 或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言),在申明一个变量的时候,并不需要显示指明它保存的数据的类型。比如:$a = 1; (整形) $a ="1";(字符串) 一直使用PHP,但它究竟什么,底层是怎么实现才成就了PHP这样方便快捷的弱类型语言。 最近也查阅了很多书籍,还.

cyb_17302190874的博客 183

Android系统开机时间分析与优化指南

本文系统阐述了Android开机时间优化的全流程技术方案。首先详细解析了从电源键按下到桌面显示的完整启动流程及关键事件序列,然后介绍了秒表计时、日志分析和自动化脚本三种测量方法。重点从底层和上层两个维度提出了具体优化措施:底层包括编译器优化、内核驱动裁剪和日志精简;上层涵盖系统服务参数调整、应用预优化和功能组件精简。最后提供了验证工具和方法,并强调优化需要在性能、功耗和功能间取得平衡。通过这套系统化方案,可有效提升Android设备的开机速度与用户体验。

学无止境 350

PHP内核的存储机制

PHP内核的存储机制 或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言),在申明一个变量的时候,并不需要显示指明它保存的数据的类型。比如:$a = 1; (整形) $a =“1”;(字符串) 一直使用PHP,但它究竟什么,底层是怎么实现才成就了PHP这样方便快捷的弱类型语言。 最近也查阅了很多书籍,还有相关博客资料,了解到了许多关于PHP内核的一些机制。 php简单的理解就是一个c语言的类库,你去php.net 下

weixin_43503815的博客 271

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

彻底搞懂PHP Session:从内核源码到会话存储的完整指南

PHP Session是Web开发中维持用户状态的核心机制,它允许服务器在多个请求之间记住用户信息。本文将带你深入PHP内核,解析Session扩展的底层实现原理,帮助开发者从根本上理解会话管理的工作机制。 ## PHP Session的核心功能与架构 PHP Session扩展提供了完整的会话生命周期管理,包括会话创建、数据存储、过期回收等关键功能。其核心实现位于`ext/session/`

gitblog_00205的博客 769

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

深入理解PHP内核

深入理解PHP内核 本文为读书笔记原书地址 第一部分 基本原理 1、基本代码的执行 生命周期和Zend引擎 一切的开始: SAPI接口 SAPI(Server Application Programming Interface)指的是PHP具体应用的编程接口。 我们很少关心PHP解释器在哪里。虽然通过Web服务器和命令行程序执行脚本看起来很不一样, 实际上它们的工作流程是一样的。命令行参数传递给P...

拥的博客 973

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

PHP7 的内核结构

新的 Zend Engine 3.0 提供了更高效的执行引擎,改进的内存管理和优化的对象模型提高了执行速度。要深入了解 PHP7 的内核,我们需要探讨其设计和实现的关键方面,包括 PHP 的执行模型、内存管理、编译和优化过程等。:PHP7 兼容多个调试工具和扩展,如 Xdebug 和 Zend Debugger,这些工具提供了强大的调试功能,帮助开发人员调试和优化代码。:PHP7 对对象模型进行了优化,改进了对象的内存布局,减少了对象操作的开销。:对象在内存中以结构体的形式存储,包含对象的属性和方法。

Crazy_shark的博客 1439

PHP内核--探究内存管理与缓存机制

PHP的内存管理可以被看作是分层(hierarchical)的。 它分为三层:存储层(storage)、堆层(heap)和接口层(emalloc/efree)。 存储层通过 malloc()、mmap() 等函数向系统真正的申请内存,并通过 free() 函数释放所申请的内存。  存储层通常申请的内存块都比较大,这里申请的内存大并不是指storage层结构所需要的

hann的专栏 4993

二十六、PHP内核探索:变量的存储 ☞ 变量存储在一个HashTable实现的符号表里

PHP中的变量类型和值是通过C语言实现的,内核具体是如何组织用户在PHP中定义的变量呢? Hash Table在ZE内核中被广泛使用,PHP变量也正是存储在一个HashTable实现的符号表里。当在PHP中调用一个函数或者类时,内核会创建一个新的符号表,这也是为什么在函数中无法使用函数外部定义的变量的原因。因为他们分属两个符号表,一个当前作用域,一个全局作用域。 现在来看内核中是如何定义作用域...

进阶之旅 322

PHP7内核剖析 学习笔记 第九章 PHP基础语法的实现

zend_brk_cont_element结构保存在zend_op_array->brk_cont_array数组中(每个循环的结构都存在里面,包括非嵌套的循环和每层嵌套的循环),zend_op_array->last_brk_cont是此数组中第一个可用位置,每申请一个zend_brk_cont_element结构,last_brk_cont就加1,然后将数组扩容。zend_brk_cont_element的parent字段记录的就是父层循环的zend_brk_cont_element在数组中的位置。

tus00000的博客 909

php底层内核回顾总结

前言: 去年曾对php底层做过研究,也没有系统的用自己的话总结一下,简单明了,故特此发文,如有不对大家指正哈。 具体的大家可以回顾专栏,里边有详细介绍 主要内容: 为什么是弱类型? php的生命周期是如何一个流程, 变量的世纪存储形式与分离改变, 内存开辟与新垃圾回收 命名空间的应用以及php为什么是弱类型 一。如何理解php是弱类型语言 1.从变量角度:在

hann的专栏 1738

PHP内核分析-变量+引用+写时复制+变量分离

变量 PHP5 变量的内部实现 变量存储结构: typedef struct _zval_struct zval; ... struct _zval_struct { /* Variable information */ zvalue_value value; /*变量的值的存放容器*/ zend_uint refcount__gc; ...

qq_36557960的博客 396

变量改变时PHP内核做了些什么?

引言 内容来自于《Extending and Embedding PHP》- Chaper 3 - Memory Management,加上自己的理解,对php中变量的引用计数、写时复制,写时改变,写时复制和改变做个”翻译“。 zval 看下面的内容之前先对zval这个结构体做个了解 typedef struct _zval_struct { zvalue_value value; ...

weixin_30919571的博客 102
上一篇: 十个最适合 Web 和 APP 开发的 NodeJS 框架
下一篇: php底层原理之垃圾回收机制(版本一)
chenrui310
博客等级 码龄15年 165粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值