PHP在内核中是通过zval这个结构体来存储变量的,它的定义在Zend/zend.h文件里
在《php7 zval及变量存储方式》的2.3节中我们说到,对于复杂类型的变量(string,array,object,resource等),我们会将其具体的值记录在单独的内存区域,再由zend_value中相应的指针指向该内存区域。指向该内存区域的指针数量,即为引用计数。
引用计数基本知识 每个php变量存在一个叫"zval"的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用。第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope),那些主脚本(比如:通过浏览器请求的的脚本)和每个函数或者方法也都有作用域。
在《php7引用计数》的文章中,我们知道,对于复制类型的变量,在赋值时,我们并没有重新复制一份数据,而是让新变量的zend_value中相应的指针指向原来的数据,同时增加引用计数。
在平时php-fpm的时候,可能很少人注意php的变量回收,但是到swoole常驻内存开发后,就不得不重视这个了,因为在常驻内存下,如果不了解变量回收机制,可能就会出现内存泄露的问题,本文将一步步带你了解php的垃圾回收机制,让你写出的代码不再内存泄漏
PHP的垃圾回收机制是自动的,它通过内置的垃圾回收器(Garbage Collector)来实现。当一个PHP对象不再被引用时,它就成为垃圾。垃圾回收器会定期扫描内存中的所有对象,将没有引用的对象标记为垃圾,并释放它们占用的内存空间,以便其他对象可以使用这些空间。
垃圾回收,简称gc。顾名思义,就是废物重利用的意思。再说这个之前先接触一下内存泄露,大概意思就是申请了一块地儿拉了会儿屎,拉完后不收拾,那么那块儿地就算是糟蹋了,地越用越少,最后一地全是屎。说到底一句,用了记得还。一定程度上说,垃圾回收机制就是用来擦屁股的。 如果用过C语言,那么申请内存的方式是malloc或者是calloc,然后你用完这个内存后,一定不要忘了用free函数去释放掉,这就是传说中手动垃圾回收,一般都是扫地神僧用这种方式。 很多高层次语言中,你这辈子都是接触不到内存管理的,比如世界上最好的语言php,这种语言替你管理了内存,你就安安心心写烂代码即可。写php的,你说你关心内存,我是不怎么相信的,一定是你在装逼。当然了,如果你用的swoole或者wm或者自己发明的常驻内存级php应用,那你将不得不关注内存泄露问题,也就说一定要记得释放无用变量。那么,在用的最普遍地最传统的web开发中,php的自动垃圾回收机制是怎样的呢? 这个问题我们先这么想,就是都知道php是C语言实现的,现在把C语言给你放在这里了,然后你想想如何用C语言实现对一个变量的统计以及释放。你不要想如何实现php,你就想C语言如何实现一个变量,从声明开始到最后没人用了,就把这个变量所占的内存给释放掉。你从这个角度出发,就会舒服一些,这不再是一个技术难题,而是一个傻逼产品经理提的一个傻逼需求。好了,步入正题,PHP进行内存管理的核心算法一共两项:一是引用计数,二是写时拷贝,请理(bei)解(song)。当你声明一个PHP变量的时候,C语言就在底层给你搞了一个叫做zval的struct(结构体);如果你还给这个变量赋值了,比如“hello world”,那么C语言就在底层再给你搞一个叫做zend_value的union(联合体),总体看来就是这样的:
1、给对象添加引用计数器,每次在某个地方引用计数器的值都会增加。每当引用失效时,计数器的值就会减一。
官网的解答如下 每个php变量存在一个叫”zval”的变量容器中一个zval变量容器,除了包含变量的类型和值 ,还包括两个字节的额外信息 is_ref 和 refcount is_ref 是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来 refcount 用以表示指向这个zval变量容器的变量个数 PHP5 中的引用计数在PHP5中,zval 的内存是单独从堆(heap)中分配的(有少数例外情况),PHP 需要知道哪些 zval 是正在使用的,哪些是需要释放的。所以这就需要用到引用计数:zval 中 refcount__gc 的值用于保存 zval 本身被引用的次数,比如 b = 12语句中,12 被两个变量引用,所以它的引用计数就是 2。如果引用计数变成 0,就意味着这个变量已经没有用了,内存也就可以释放了。
定义变量之后,内存增加,清除变量之后,内存恢复(有些可能不会恢复和以前一样),好像定义变量时申请了一次内存,其实不是这样的,php会预先申请一块内存,不会每次定义变量就申请内存。 首先我们要打破一个思维: PHP不像C语言那样, 只有你显示的调用内存分配相关API才会有内存的分配. 也就是说, 在PHP中, 有很多我们看不到的内存分配过程. 比如对于: $a = "laruence"; 隐式的内存分配点就有: 1.1. 为变量名分配内存, 存入符号表 2.2. 为变量值分配内存 所以, 不能只看表象. 第二, 别怀疑,PHP的unset确实会释放内存, 但这个释放不是C编程意义上的释放, 不是交回给OS. 对于PHP来说, 它自身提供了一套和C语言对内存分配相似的内存管理API:
垃圾回收机制是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。在现在的流行各种语言当中,垃圾回收机制是新一代语言所共有的特征,如Python、PHP、C#、Ruby等都使用了垃圾回收机制。
注意:php5.3中将一个变量 = 赋值给另一个变量时,不会立即为新变量分配内存空间,而是在原变量的zval中给refcount加1。只有当原变量或者发生改变时,才会为新变量分配内存空间,同时原变量的refcount减 1 。当然,如果unset原变量,新变量直接就使用原变量的zval而不是重新分配。&引用赋值时,原变量的is_ref 加1. 如果给一个变量&赋值,之前 = 赋值的变量会分配空间。
php垃圾回收机制,对于PHPer来说是一个不陌生但是又不是很熟悉的内容。那么php是怎么实现对不需要的内存进行回收的呢?
php 的变量存储在「zval」变量容器(数据结构)中,「zval」属性包含如下信息:
在PHP的数据结构中,引用计数就是指每一个变量,除了保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数。为什么要多保存这样两个内容呢?当然是为了垃圾回收(GC)。也就是说,当引用次数为0的时候,这个变量就没有再被使用了,就可以通过 GC 来进行回收,释放占用的内存资源。任何程序都不能无限制的一直占用着内存资源,过大的内存占用往往会带来一个严重的问题,那就是内存泄露,而 GC 就是PHP底层自动帮我们完成了内存的销毁,而不用像 C 一样必须去手动地 free 。
最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑。 仔细一想不难发现问题所在:这些文章大多是 PHP5.x 时代的,而 PHP7 发布后,采用了新的 zval 结构,相关的资料也比较贫瘠,所以我结合一些资料做了一个总结, 主要侧重于解释新 zval 容器中的引用计数机制 ,如有谬误,还望不吝指教。
但是当新的变量值变更时 , 值从新赋予新的值时 , 就会减掉刚才的引用计数,并且从新创建内存空间.
大家好,我是黄啊码,相信java的垃圾回收机制,任何java入门的码农们多多少少已经接触过了,那么php的垃圾回收机制又有多少知道,知道的评论区打个1呗。
注意,xdebug_debug_zval函数是xdebug扩展的,使用前必须安装xdebug扩展,输出如下
此前我们了解过 java 和 python 如何管理内存以及运行过程中的垃圾收集。 python 的内存管理与垃圾收集 java 的存活判定与垃圾收集
写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改。 COW最早应用在*nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C++的STL等。 在PHP内核中,COW也是主要的内存优化手段。 在前面关于变量和内存的讨论中,引用计数对变量的销毁与回收中起着至关重要的标识作用。 引用计数存在的意义,就是为了使得COW可以正常运作,从而实现对内存的优化使用。
PHP目前的开发框架,除了主流常用的FPM框架,想必就是基于swoole拓展的常驻内存开发了。
PHP 是一门托管型语言,在 PHP 编程中,程序员不需要手工处理内存资源的分配与释放(使用 C 编写 PHP 或 Zend 扩展除外),这就意味着 PHP 本身实现了垃圾回收机制(Garbage Collection)。在 PHP 官方网站可以看到对垃圾回收机制的介绍。
按正常理解php的变量是引用计数,第一次创建变量refcount会是0,当把这个变量赋给新的变量时,refcount会加1
PHP中的引用就是两个变量指向了同一个地方,只要在变量前面增加了&符号,它就变成了一个引用
在php中的变量占用的空间,是不需要我们手动回收的。内核帮我们处理了这一部分的工作。相比C,这大大方便了我们的操作。
进程在运行时,所操作的内存就是虚拟内存,每个进程之间的虚拟内存互相独立,通过 MMU 内存管理技术再映射到物理内存中,同时,虚拟内存空间块分为:
这个主要是输出变量的数据值,特别是数组合对象数据,一般我们在查看端口的返回值或者不确定的变量可以使用这两个API,debug_zval_dump类似,唯一增加的一个值是refcount,记录一个变量被引用了多少次
上周战队知识分享时,H3018大师傅讲了PHP GC回收机制的利用,学会了如何去绕过抛出异常。H3018大师傅讲述的很清楚,大家有兴趣的可以去看一下哇,链接如下https://www.bilibili.com/video/BV16g411s7CH/这里没有怎么涉及底层原理,只是将我自己的见解简述一下,希望能对正在学习PHP反序列化的师傅有所帮助。
上周战队知识分享时,H3018大师傅讲了PHP GC回收机制的利用,学会了如何去绕过抛出异常。H3018大师傅讲述的很清楚, 大家有兴趣的可以去看一下哇,链接如下 https://www.bilibili.com/video/BV16g411s7CH/ 这里没有怎么涉及底层原理,只是将我自己的见解简述一下,希望能对正在学习PHP反序列化的师傅有所帮助。
静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在静态存储区内分配存储单元)。
输出变量的数据值,特别是数组和对象数据,一半在查看接口的返回值或者不确定的变量可以使用这两个api,debug_zval_dump输出结果和var_dump类似,位移增加的值是refcount,记录一个变量被引用了多少次
网上关于 PHP 的资料多如牛毛,关于其核心 Zend Engine 的却少之又少。PHP 中文手册出现已 N 年,但 Zend API 的翻译却仍然不见动静,小弟自觉对 Zend Engine 略有小窥,并且翻译也有助于强迫自己对文章的进一步理解,于是尝试翻译此章,英文不好,恭请方家指点校核。转载请注明来自抚琴居(译者主页):http://www.yAnbiN.org
3. 打开phpStorm,快捷键Clt+Alt+S打开settings搜索Xdebug.
Xdebug是PHP的扩展,用于协助调试和开发。 – 它包含一个用于IDE的调试器 – 它升级了PHP的var_dump()函数 – 它为通知,警告,错误和异常添加了堆栈跟踪 – 它具有记录每个函数调用和磁盘变量赋值的功能 – 它包含一个分析器 – 它提供了与PHPUnit一起使用的代码覆盖功能。
设置的地方在tool->option里找到debug标签,在这里可以修改PHP.EXE的路径,还有调试的工具和端口号等。
Xdebug是一个开放源代码的PHP程序调试器,其实就是一个Debug工具而已。可以用来跟踪,调试、分析PHP程序当前的运行状况!Xdebug作为PHP调试工具,提供了丰富的调试函数,通过开启自动跟踪(auto_trace)和分析器功能,可以比较直观的看到PHP源代码的性能数据,这为优化PHP代码提供了大大的方便。
比如下载的是32位的TS版本:php_xdebug-2.4.1-5.4-vc9.dll,这个文件复制进任意目录都可以。
PHP_ARG_ENABLE(hello, [whether to enable Hello World support],
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说宝塔 开启xdebug_Xdebug 使用说明,希望能够帮助大家进步!!!
接口代码在调试时,经常是print_r或者var_dump来断点,但是当项目较为复杂的情况下,这么做效率就非常低下了,断点调试就非常好的解决了这个问题。一开始可能不太适应断点调试,但是当习惯之后,越用越舒服。
PHP7已经发布, 如承诺, 我也要开始这个系列的文章的编写, 主要想通过文章让大家理解到PHP7的巨大性能提升背后到底我们做了什么, 今天我想先和大家聊聊zval的变化. 在讲zval变化的之前我们先来看看zval在PHP5下面是什么样子
曾几何时php一不小心闯入了我生活,php语法竟然和C语言那么莫名的相似,这是最初php给我的感受,当接触的php时间越来越多的时候,php也没有那般生涩难懂,但是偶尔一些的新的php 设计思想,也会思考许久,不知是从什么时候开始了php另一个世界。我想应该是从那次的类型转换开始的,"1e12"字符串类型在转化为数字类型变量时,不同的php版本下转换结果截然不同,有的就变成了数字1,有的却可以正常的识别为科学计数法10^12,在这个地方就已经悄悄的埋下了一枚种子。
前言: php代码在调试时,经常是print_r或者var_dump来断点,但是当项目较为复杂的情况下,这么做效率就非常低下了,断点调试就非常好的解决了这个问题。一开始可能不太适应断点调试,但是当习惯之后,越用越舒服。
领取专属 10元无门槛券
手把手带您无忧上云