PHP的垃圾回收机制之引用计数
1,介绍
php的垃圾回收机制(GC)是在PHP5之后出现的,而在PHP5.3版本之前使用的都是“引用计数”的方式。实现引用计数的实质就是在每个内存对象中都有一个计数器,当内存对象被变量引用时,计数器+1,当变量取消或更改引用内存时,计数器-1,直到计数器中的值为0时,说明该内存对象没有被变量引用,,就产生了一个无意义的内存对象。php就会销毁释放内存,进行垃圾回收。
2,实现
每个变量被赋值时就会生成叫‘zval’的变量容器,‘zavl’容器中除了包含变量名,变量的类型和变量的值以外,还包含两个字节的额外信息。第一个是"is_ref",是个布尔值,用来表示该变量是否被引用,php就是根据这个判断变量是否是普通类型还是引用类型;另一个是‘refcount’,功能类似于计数器,记录指向该变量容器的变量个数。
1,生成并显示一个新的zval容器
$a;
xdebug_debug_zval('a');//a: no such symbo
$b="new string";
xdebug_debug_zval('b');//b: (refcount=1, is_ref=0),
可以看到:变量赋值后生成的‘zavl’容器中的参数。其中refcount=1表示一个变量指向该变量容器。
2,添加一个‘zavl’的引用计数
$b="new string";
xdebug_debug_zval('b');
$c=$b;
xdebug_debug_zval('b');//b: (refcount=2, is_ref=0),string 'new string' (length=10)
$d="new string";
xdebug_debug_zval('d');//d: (refcount=1, is_ref=0),
这时,引用次数是2,因为同一个变量容器被变量b和变量c关联。当将一个变量赋值给另一个变量时,php不会去复制已生成的变量容器。而将一个值赋值给一个变量时,会重新生成一个新的‘zavl’容器。
3,清空并删除一个引用计数
$b="new string";
$c=$b;
unset($b);
xdebug_debug_zval('c');//c:(refcount=1, is_ref=0),string 'new string' (length=10)
unset($c);
xdebug_debug_zval('c');//c: no such symbol
当任何关联到某个变量容器的变量离开它的作用域(比如:函数执行结束),或者对变量调用了函数 unset()时,”refcount“就会减1。当refcount=0时,包含类型和值的这个变量容器就会从内存中删除。
上面的数据类型都是标量数据类型,而复合数据类型(array,object)又有不同。
4,生成一个复合数据的变量容器
$arr=[];
xdebug_debug_zval('arr');
$array=['name'=>'张三','age'=>'18'];
xdebug_debug_zval('array');
结果如下:

图示:

可以看到:数组分配了三个zval容器:array name age
5,添加一个已经存在的元素到数组中
$array = [
'name' => '张三',
'age' => '18'
];
$array['zhangsan'] = $array['name'];
xdebug_debug_zval('array');

6,销毁包含数组名信息的'zavl'变量容器
$array = [
'name' => '张三',
'age' => '18'
];
$array['zhangsan'] = $array['name'];
unset($array);
xdebug_debug_zval('array');
其结果如下

删除数组中的一个元素,就是类似于从作用域中删除一个变量. 删除后,数组中的这个元素所在的容器的“refcount”值减少,同样,当“refcount”为0时,这个变量容器就从内存中被删除。
一般来说,'zavl'容器中的refcount=0时,该变量容器就是垃圾,会被php引擎销毁。但是有一种情况:'zavl'容器中的refcount不为0,却没有变量指向它,仍然是个垃圾。
环状引用就是一个例子,即把数组作为一个元素添加到自己。
7,生成一个包含环状引用的‘zavl’容器
$arr = [
'name' => '张三'
];
$arr[] = &$arr;
xdebug_debug_zval('arr');
结果如下

图示:

8,删除环状引用
$arr = [
'张三'
];
$arr[] = &$arr;
unset($arr);
xdebug_debug_zval('arr');
图示:

现在,虽然没有任何变量指向该‘zavl’,但由于数组元素“0”仍然指向数组本身,所以该‘zavl’不能被清除 。而又因为没有另外的变量指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。庆幸的是,php脚本运行完毕后会清除这个数据结构。但是在php清除之前,将耗费不少空间的内存。而在一般情况下,php脚本运行完毕因此环状引用内存泄露的问题一般只影响长时间运行的程序脚本。并且在5.3版本后垃圾处理虽然其基础仍然是引用计数,但是做了一些改良,能够将环状引用导致的内存泄露控制在一定的规模以内。
当然,我们也可以手动的清除环状结构
9,手动清除环状引用
$arr = [
'张三'
];
$arr[] = &$arr;
$arr = null;
xdebug_debug_zval('arr');
unset($arr);
xdebug_debug_zval('arr');
结果如下

$arr=null后,虽然变量容器‘zavl’还存在,但是之前储存在内存中引用传递的值却是完全删除掉了,最后再使用unset($arr)就可以断开变量名arr和null值之间的联系,令refcount=0就能够释放到内存了。
PHP的垃圾回收机制之引用计数的更多相关文章
- Python的垃圾回收机制(引用计数+标记清除+分代回收)
一.写在前面: 我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念.在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就是一个结构体--PyOb ...
- Python的垃圾回收机制以及引用计数
Python中的计数引用 在Python中,由于Python一门动态的语言,内部采用的指针形式对数据进行标记的,并不像c/c++那样,通过指定的数据类型并分配相应的数据空间,Python中定义的变量名 ...
- c++垃圾回收代码练习 引用计数
学习实践垃圾回收的一个小代码 采用引用计数 每次多一个指针指向这个分配内存的地址时候 则引用计数加1 当计数为0 则释放内存 他的难点在于指针之间的复制 所有权交换 计数的变化 #include &l ...
- PHP GC垃圾回收机制之引用变量回收周期疑问
普通的引用变量的销毁大家都知道, 当unset的时候如果refcount = 0 则认为无用, 销毁. 但是手册中提到一点会有递归引用的问题,很是奇葩 代码如下 <?php $a = 1; $a ...
- Python垃圾回收详解:引用计数+标记清理+分代回收
Python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略. 1.引用计数 python中一切皆对象,所以python底层计数结构地就可以抽象为: 引用计数结构体{ 引用计数; 引用 ...
- php 调试工具及学习PHP垃圾回收机制了解引用计数器的概念
php代码工具:Xdebug 与分析工具 WinCacheGrind Xdebug之函数大全: string xdebug_call_class()返回当前被调用的函数或方法所属的类的类名 stri ...
- 学习PHP垃圾回收机制了解引用计数器的概念
php变量存在一个叫"zval"的变量容器中,"zval"变量容器包括含变量的类型和值,还包括额外的两个字节信息,分别是“is_ref”表示变量是否属于引用,“ ...
- python垃圾回收机制与小整数池
python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...
- JavaScript中的垃圾回收机制与内存泄露
什么是内存泄露? 任何编程语言,在运行时都需要使用到内存,比如在一个函数中, var arr = [1, 2, 3, 4, 5]; 这么一个数组,就需要内存. 但是,在使用了这些内存之后, 如果后面他 ...
随机推荐
- ESA2GJK1DH1K升级篇: 移植远程更新程序到STM32F103RET6型号的单片机,基于(GPRS模块AT指令TCP透传方式)
前言 上节实现远程更新是更新的STM32F103C8T6的单片机 GPRS网络(Air202/SIM800)升级STM32: 测试STM32远程乒乓升级,基于(GPRS模块AT指令TCP透传方式),定 ...
- day 18
Only through experience of trial and suffering can the soul be strengthened, vision cleared, ambitio ...
- 深入js系列-类型(开篇)
思考 作为一个编程人员,你可能从来没仔细思考过,为什么这么多高级语言会有类型这东西. 实际上,类型有点类似生活中的类别,我们日常生活,早已经把这个概念理解到了,切肉和切水果会用不同的刀. 语言级别的类 ...
- 【BigData】Java基础_创建一个订单类
需求描述 定义一个类,描述订单信息订单id订单所属用户(用户对象)订单所包含的商品(不定数量个商品对象)订单总金额订单应付金额: 总金额500~1000,打折85折 总金额1000~150 ...
- pytest学习笔记二 fixtrue
前言 官方文档关于fixture功能的解释如下: The purpose of test fixtures is to provide a fixed baseline upon which test ...
- Shell脚本之一 Shell脚本简介
一.什么是shell? 我们平时所说的 Shell 可以理解为 Linux 系统提供给用户的使用界面.Shell 为用户提供了输入命令和参数并可得到命令执行结果的环境.当一个用户登录 Linux 之后 ...
- sts问题合集
背景:用来记录在使用sts过程中遇到的相关问题 Version 当前jdk版本号 of the JVM is not suitable for the this product.Version:1.8 ...
- 将Javabean转化JSONObject为对象
JSONObject.parseObject(JSONObject.toJSON(obj).toString()):
- Windows设置国内源阿里云镜像加速与离线安装pip包的方法
Windows设置国内源阿里云镜像加速1.先在 windows “文件资源管理器” 地址栏 输入 %APPDATA% 按回车,打开程序自定义设置文件夹然后,创建名为 pip 的文件夹,用于存放 pip ...
- AQS工作原理分析
AQS工作原理分析 一.大致介绍1.前面章节讲解了一下CAS,简单讲就是cmpxchg+lock的原子操作:2.而在谈到并发操作里面,我们不得不谈到AQS,JDK的源码里面好多并发的类都是通过Sy ...