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的垃圾回收机制之引用计数的更多相关文章

  1. Python的垃圾回收机制(引用计数+标记清除+分代回收)

    一.写在前面: 我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念.在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就是一个结构体--PyOb ...

  2. Python的垃圾回收机制以及引用计数

    Python中的计数引用 在Python中,由于Python一门动态的语言,内部采用的指针形式对数据进行标记的,并不像c/c++那样,通过指定的数据类型并分配相应的数据空间,Python中定义的变量名 ...

  3. c++垃圾回收代码练习 引用计数

    学习实践垃圾回收的一个小代码 采用引用计数 每次多一个指针指向这个分配内存的地址时候 则引用计数加1 当计数为0 则释放内存 他的难点在于指针之间的复制 所有权交换 计数的变化 #include &l ...

  4. PHP GC垃圾回收机制之引用变量回收周期疑问

    普通的引用变量的销毁大家都知道, 当unset的时候如果refcount = 0 则认为无用, 销毁. 但是手册中提到一点会有递归引用的问题,很是奇葩 代码如下 <?php $a = 1; $a ...

  5. Python垃圾回收详解:引用计数+标记清理+分代回收

    Python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略. 1.引用计数 python中一切皆对象,所以python底层计数结构地就可以抽象为: 引用计数结构体{ 引用计数; 引用 ...

  6. php 调试工具及学习PHP垃圾回收机制了解引用计数器的概念

    php代码工具:Xdebug  与分析工具 WinCacheGrind Xdebug之函数大全: string xdebug_call_class()返回当前被调用的函数或方法所属的类的类名 stri ...

  7. 学习PHP垃圾回收机制了解引用计数器的概念

    php变量存在一个叫"zval"的变量容器中,"zval"变量容器包括含变量的类型和值,还包括额外的两个字节信息,分别是“is_ref”表示变量是否属于引用,“ ...

  8. python垃圾回收机制与小整数池

    python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...

  9. JavaScript中的垃圾回收机制与内存泄露

    什么是内存泄露? 任何编程语言,在运行时都需要使用到内存,比如在一个函数中, var arr = [1, 2, 3, 4, 5]; 这么一个数组,就需要内存. 但是,在使用了这些内存之后, 如果后面他 ...

随机推荐

  1. Chrome DevTools的使用

    一.Chrome DevTools 简介 Chrome 开发者工具是一套内置于Google Chrome中的Web开发和调试工具,可用来对网站进行迭代.调试和分析 手册:Chrome 开发者工具中文手 ...

  2. jedis代码操作

    一.jedis快速入门 * Jedis: 一款java操作redis数据库的工具. * 使用步骤: 1. 下载jedis的jar包 2. 使用 //1. 获取连接 Jedis jedis = new ...

  3. QT QWidget 关闭的流程

    当QWidget被点击右上角“X”关闭时: 1.调用虚函数closeEvent 2.调用QWidget的析构函数

  4. ReentrantLock源码简析

    概念 ReentrantLock,可重入锁.在多线程中,可以通过加锁保证线程安全. 加锁和解锁 加锁: public void lock() { sync.lock(); } 解锁 public vo ...

  5. 【Gamma】 Phylab 展示博客

    目录 [Gamma] Phylab 展示博客 发布地址 网站:PhyLab GitHub Release: WhatAHardChoice/Phylab Gamma版本 一.团队简介 二.项目目标 2 ...

  6. mysql 基本操作二

    1.查询数据 MariaDB [jason]> offset ; 默认是从偏移量为0 处开始查村数据,通过指定offset 可以从offset 处开始取数 2.where 语句 MariaDB ...

  7. Spring的JdbcTemplate使用教程

    Spring对数据库的操作在jdbc上面做了基本的封装,让开发者在操作数据库时只需关注SQL语句和查询 结果处理器,即可完成功能(当然,只使用JdbcTemplate,还不能摆脱持久层实现类的编写). ...

  8. FROM_UNIXTIME()时间戳转换函数

    前几天,工作用到了将时间戳转化成具体的时间(年月日 时:分:秒),出了一点问题,先看一下下面的sql语句: select *,FROM_UNIXTIME(created_at,'%Y-%m-%d %H ...

  9. 用FTPClient,执行到ftp.storeFile(fileName, inputFile);无反应了

    Q:用FTPClient,执行到ftp.storeFile(fileName,  inputFile):无反应了 A:  ftpclient.enterLocalPassiveMode(); ftp. ...

  10. netcore3.0 webapi集成Swagger 5.0,Swagger使用

    Swagger使用 1.描述 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 作用: 1.接口的文档在线自动生成. 2.功能测试 本文转自 ...