在编写php扩展时,似乎参数(即传给zend_parse_parameters的变量)是不需要free的。 举例:
运行正常: test("Hello World"); // 打印Hello World 这里不用担心test函数会发生内存泄露,php会自动帮我们回收这些用于保存参数的变量。 那php究竟是如何做到的呢?要解释这个问题,还是得看php是怎么传递参数的。 EG(argument_stack)简介 简单来讲,在php中的EG中保存了一个专门用于存放参数的栈,名为argument_stack。每当发生函数调用时,php会将传入的参数压进EG(argument_stack)。一旦函数调用结束,则EG(argument_stack)被清理,并且等待下一次的函数调用。 关于EG(argument_stack)的struct结构、用途,php5.2和5.3实现有一些区别。本文主要以5.2为例,5.3+的变化后面抽空再说。
上图是5.2中argument_stack的大概示意图,看起来简单明了。其中,栈顶和栈底固定为NULL。函数接收的参数按照从左到右的顺序,依次被压入栈。注意,最后会被额外压入一个long型的值,表示栈里的参数个数(上图中为10)。 那被压入argument_stack的参数究竟是什么呢?其实是一个个zval类型的指针。 它们指向的zva有可能是CV变量,有可能是is_ref=1的变量,还有可能是一个常量数字,或者常量字符串。 EG(argument_stack)在php5.2中被具体实现为zend_ptr_stack类型:
初始化argument_stack 初始化argument_stack的工作是发生在php处理具体的请求之前,更准确说是处于php解释器的启动过程之中。 在init_executor函数里我们发现如下2行:
这2行分别代表着,初始化EG(argument_stack),紧接着压入一个NULL。由于EG是个全局变量,因此在实际调用zend_ptr_stack_init之前,EG(argument_stack)中的所有数据全部为0。 zend_ptr_stack_init实现很简单。
一旦argument_stack被初始化完,则立即会被压入NULL。这里无须深究,这个NULL其实没有任何的含义。 NULL入栈之后,整个argument_stack的实际内存分布如下:
参数入栈 在压入第一个NULL之后,一旦再有参数入栈,则argument_stack会发生如下动作: stack->top++; *(stack->top_element++) = 参数; 用一段简单的php代码来说明问题:
上述代码在调用foo时,传入了一个字符串常量。因此,实际上被压入栈的是一个指向存储“hello world”的zval。用vld来查看编译之后的opcode:
SEND_VAL指令实际上做的事情就是将“hello world”压入argument_stack。
入栈完成之后的argument_stack为: 参数个数 前文说到,实际上并非把所有参数入栈就完事了。php还会额外压入一个数字,表示参数的个数,这个工作并非发生在SEND_XXX指令时。实际上,在真正执行函数之前,php会将参数个数入栈。 继续沿用上面的例子,DO_FCALL 指令用于调用foo函数。在调用foo之前,php会自动填入argument_stack最后一块。
压入参数个数和NULL之后,用于foo调用的整个argument_stack已然完成。
第一条指令是RECV,从字面上理解便是用于获取栈中参数的。实际上,SEND_VAL和RECV有点对应的感觉。每次函数调用之前SEND_VAL,在函数内部进行RECV。为什么不说是完全对应,实际上RECV指令并非一定需要。只有当用户定义的函数被调用是,才会产生RECV。我们编写的扩展函数,php自带的内建函数,都不会有RECV。 需要额外指出的是,每次SEND_VAL和RECV 均只能处理一个参数。也就是说如果传参的过程中有多个参数,那么会产生若干SEND_VAL以及若干RECV。这里引出一个很有趣的话题,传入参数和获取参数的顺序是怎样的呢? 答案是,SEND_VAL会将参数从左至右的进行压栈,而RECV一样的从左至右获取参数。
zend_assign_to_variable_reference 和 zend_receive 都会完成“获取参数” 。“获取参数”不太好理解,实际它究竟是做哪些事情呢? 说到底很简单,“获取参数”就是将这个参数添加到当前函数执行期间的“符号表”中,具体对应为EG(current_execute_data)->symbol_table。本示例中,RECV完成之后,函数体内的symbol_table中有了一个符号‘str’,它的值为“hello world”。 但argument_stack并没有发生一丝变化,因为RECV仅仅是读取参数,而不会对栈产生类似pop操作。 清理argument_stack
foo内部的print_r也是一个函数调用,因此也会产生压栈-->清栈的操作。因此print_r执行之前的argument_stack为:
print_r执行之后argument_stack又回到了foo刚RECV完的状态。 具体调用print_r的过程并非本文阐述的重点。我们关心的是当调用foo结束之后,php是如何清理argument_stack的。 上面展示的do_fcall代码片段中可以看出,清理工作由zend_ptr_stack_clear_multiple完成的。
注意这里清理栈中zval指针,使用的是zval_ptr_dtor。zval_ptr_dtor会将refcount减1,一旦refcount减为0,则保存该变量的内存区域会被真正的回收掉。 在本文示例中,foo调用完毕之后,保存“hello world”的zval状态为:
由于refcount只剩1,因此,zval_ptr_dtor会将“hello world”真正从内存中销毁。 消栈完毕之后的argument_stack内存状态为: 可以看出上图中的argument_stack与刚被初始化之后是一样的。此时argument_stack真正做好了迎接下一次函数调用的准备。 回到文章刚开始的问题... 为何无需free(str)呢?弄明白了argument_stack之后就很好理解这个问题了。 因为str指向的是zval中实际存放“hello world”的内存地址。假设扩展函数如下:
则调用
会输出“Hello world”。尽管我们调用test时,并非是传$a的引用,但实际效果相当于test(&$a)。 简单来说,内存中只有一份$a,不管是CV数组中,还是在argument_stack中。而zend_parse_parameters并没有拷贝一份数据用于函数执行,事实上它也不能这么做。因此,当函数完毕之后,如果没有其他地方会用到$a,php清理argument_stack时会帮我们free。如果仍然其他代码在使用,就更加不能手动free了,否则会破坏$a的内存区域。 注意,并非写扩展函数中用到的每个变量,php都会自动回收。所以该free时,切勿手软:) |

PHP在现代Web开发中仍然重要,尤其在内容管理和电子商务平台。1)PHP拥有丰富的生态系统和强大框架支持,如Laravel和Symfony。2)性能优化可通过OPcache和Nginx实现。3)PHP8.0引入JIT编译器,提升性能。4)云原生应用通过Docker和Kubernetes部署,提高灵活性和可扩展性。

PHP适合web开发,特别是在快速开发和处理动态内容方面表现出色,但不擅长数据科学和企业级应用。与Python相比,PHP在web开发中更具优势,但在数据科学领域不如Python;与Java相比,PHP在企业级应用中表现较差,但在web开发中更灵活;与JavaScript相比,PHP在后端开发中更简洁,但在前端开发中不如JavaScript。

PHP和Python各有优势,适合不同场景。1.PHP适用于web开发,提供内置web服务器和丰富函数库。2.Python适合数据科学和机器学习,语法简洁且有强大标准库。选择时应根据项目需求决定。

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP成为许多网站首选技术栈的原因包括其易用性、强大社区支持和广泛应用。1)易于学习和使用,适合初学者。2)拥有庞大的开发者社区,资源丰富。3)广泛应用于WordPress、Drupal等平台。4)与Web服务器紧密集成,简化开发部署。

PHP在现代编程中仍然是一个强大且广泛使用的工具,尤其在web开发领域。1)PHP易用且与数据库集成无缝,是许多开发者的首选。2)它支持动态内容生成和面向对象编程,适合快速创建和维护网站。3)PHP的性能可以通过缓存和优化数据库查询来提升,其广泛的社区和丰富生态系统使其在当今技术栈中仍具重要地位。

在PHP中,弱引用是通过WeakReference类实现的,不会阻止垃圾回收器回收对象。弱引用适用于缓存系统和事件监听器等场景,需注意其不能保证对象存活,且垃圾回收可能延迟。

\_\_invoke方法允许对象像函数一样被调用。1.定义\_\_invoke方法使对象可被调用。2.使用$obj(...)语法时,PHP会执行\_\_invoke方法。3.适用于日志记录和计算器等场景,提高代码灵活性和可读性。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

Atom编辑器mac版下载
最流行的的开源编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

PhpStorm Mac 版本
最新(2018.2.1 )专业的PHP集成开发工具

Dreamweaver CS6
视觉化网页开发工具

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),