PHP - 垃圾回收机制收集

发布于:2022-12-28 ⋅ 阅读:(276) ⋅ 点赞:(0)

1.应用场景

了解学习PHP[内存]垃圾回收机制, 帮助理解编程语言的运行机制, 提高开发水平.

2.操作/学习

垃圾回收机制 的图像结果

1.文档

PHP的垃圾回收机制以及大概实现 - 王召波 - 博客园

PHP垃圾回收机制理解 - 张宇航 - 博客园

PHP: 垃圾回收机制 - Manual -- 【基本上】任何时候,都先把官网上的介绍看完了,再看其他的知识点

2. 整理输出

2.1 介绍

垃圾回收是一个多数编程语言中都带有的内存管理机制。

与非托管性语言相反:C, C++ 和 Objective C,用户需要手动收集内存,

带有 GC 机制的语言:Java, javaScript 和 PHP 可以自动管理内存。

垃圾回收机制(GC)顾名思义,就是废物重利用的意思,是一种动态存储分配的方案。

它会自动释放程序不再需要的已分配的内存块。

垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。

在现在的流行各种语言当中,垃圾回收机制是新一代语言所共有的特征,如: Python、PHP、C#、Ruby等都使用了垃圾回收机制。

2.2 实现方式

1. 在PHP5.3版本之前, 使用的垃圾回收机制是单纯的“引用计数”。

2. 随着PHP的发展,PHP开发者的增加以及其所承载的业务范围的扩大,在PHP5.3中引入了更加完善的垃圾回收机制,新的垃圾回收机制【引用计数 + 根缓存区机制】解决了无法处理循环的引用内存泄漏问题。

PHP进行内存管理的核心算法一共两项:

一是引用计数,二是写时拷贝。

请(please)理(bei)解(song)

第一项. 引用计数

使用的是“引用计数”方式进行回收。


简单地理解的话,

就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存。

当指向该片内存的指针数量为0,那么该片内存区域就可以被回收。

引用计数计数简单,强大,但是有一个致命的缺陷,就是环状引用。

<?php


$a = [];
$a[] = &$a; // 第一次留意这样使用
unset($a);

变量$a引用了自己,形成一个环。

$a被unset,但由于存在环状引用,因此$a之前指向的内存的引用计数为1,因此该内存区域不会被垃圾回收机制回收。
 
PHP 5.3 针对这个重大的缺陷做了优化。

虽然其基础仍然是引用计数,但是在做了一些改良,能够将 环状引用导致的内存泄露控制在一定的规模以内。
当然,这并不是说你可以随便滥用内存,编写代码时仍然要小心为上!// This is the key point !!


其他要点:
1. PHP脚本运行完毕,该脚本申请的所有内存空间都会释放,不管是否存在环状引用。

因此环状引用内存泄露的问题一般只影响长时间运行的程序脚本。 

补充:

会在大并发量下,执行期间,内存也会飙升,因为内存尚未释放,尤其是大对象/数组的数据类型

2. 垃圾回收机制需要满足一定的条件才会执行。因此unset后,系统并不一定会立即回收垃圾

3. unset的作用。
unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1”

也就是说,如果有一个以上的变量指向同一个内存区域,或者存在环状引用,那么unset不会使内存区域释放。

断开也说明unset并不会直接删除内存区域,而只是改变其引用计数而已。

4. $xx = null的作用。
$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0。

根据我对这个定义的理解,= null操作可以立即释放掉内存空间!

因此很多PHP技巧中不厌其烦地对我们说,先将变量设为null,再unset。【问题:这种情况,引用计数归0,再unset,会怎样?

理解其深层原理后,我才彻底理解了这样做的原因!= null 才是王道!

第二项. 写时拷贝

即:

变量的普通赋值与引用赋值

两者的区别:

PHP变量的赋值和引用以及传值与传引用的区别_william_n的博客-CSDN博客

<?php


// 先不要问为什么非要加mt_rand,不然,绝笔说不过来了,到处都是坑


$a = 'hello' . mt_rand( 1, 1000 );
$b = $a;
$a = 123;
echo $b . PHP_EOL;

运行结果,不用我说吧,脚趾头都知道是'hello'.mt_rand( 1, 1000 )的结果,绝对不可能是123。

其实,当你把$a赋值给$b的时候,$a的值并没有真的复制了一份,这样是对内存的极度不尊重,

也是对时间复杂度的极度不尊重,计算机仅仅是将$b指向了$a的值而已,这就叫多快好省

那么,什么时候真正的发生复制呢?

就是当我们修改$a的值为123的时候,这个时候就不得已进行复制,避免$b的值和$a的一样。

Q: 什么样的情况会导致zend_value的refcount不为0,但是这个zend_value却是个垃圾呢?

A: PHP7 两种情况:

  1. 数组:数组的某个成员使用&引用自己

  2. 对象:对象的某个成员引用对象自己

    请理(bei)解(song),一般面试,你能回答到这一步,已经非常屌了!

Note:

Redis中AOF日志重写也用到了写时复制技术

有些技术,在很多组件/服务中都有用到,要学会总结,类比。

2.3 回收周期 与 回收函数/方式

默认的,PHP的垃圾回收机制是打开的,然后有个php.ini设置允许你修改它:zend.enable_gc 。

当垃圾回收机制打开时,算法会判断每当根缓存区存满时,就会执行循环查找

根缓存区有固定的大小,默认10,000,可以通过修改PHP源码文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,然后重新编译PHP,来修改这个值。

当垃圾回收机制关闭时,循环查找算法永不执行,然而,根将一直存在根缓冲区中,不管在配置中垃圾回收机制是否激活。---- 这句话是什么意思?TBD

除了修改配置zend.enable_gc ,也能通过分别调用gc_enable() 和 gc_disable()函数运行PHP时来打开和关闭垃圾回收机制。

调用这些函数,与修改配置项来打开或关闭垃圾回收机制的效果是一样的。

即使在可能根缓冲区还没满时,也能强制执行周期回收。

你能调用gc_collect_cycles()函数达到这个目的。

这个函数将返回使用这个算法回收的周期数。--- 执行这个函数是否会有其他问题,比如伴随的副作用?参见4.性能第2点

允许打开和关闭垃圾回收机制并且允许自主的初始化的原因,是由于你的应用程序的某部分可能是高时效性的。在这种情况下,你可能不想使用垃圾回收机制。

当然,对你的应用程序的某部分关闭垃圾回收机制,是在冒着可能内存泄漏的风险,因为一些可能根也许存不进有限的根缓冲区。

因此,就在你调用gc_disable()函数释放内存之前,先调用gc_collect_cycles()函数可能比较明智。因为这将清除已存放在根缓冲区中的所有可能根,然后在垃圾回收机制被关闭时,可留下空缓冲区以有更多空间存储可能根。

使用哪些函数可以进行垃圾回收

gc_collect_cycle()

unset // 不会一定会清理变量,会让引用计数减1,从而判断是否为0....

赋值null // 直接将$a 指向的数据结构置空,同时将其引用计数归0 -- 虽然可以被回收,但是不是立马回收,而是等待下一个回收周期 -- 参见下面红色

所以可以结合: 先将变量赋值null,然后执行unset()

4. 性能影响

PHP: 性能方面考虑的因素 - Manual

在上一节我们已经简单的提到:回收可能根有细微的性能上影响,但这是把PHP 5.2与PHP 5.3比较时才有的。尽管在PHP 5.2中,记录可能根相对于完全不记录可能根要慢些,而PHP 5.3中对 PHP run-time 的其他修改减少了这个性能损失。

这里主要有两个领域对性能有影响。第一个是内存占用空间的节省,另一个是垃圾回收机制执行内存清理时的执行时间增加(run-time delay)。我们将研究这两个领域。

1、内存占用空间的节省
首先,实现垃圾回收机制的整个原因是为了一旦先决条件满足,通过清理循环引用的变量来节省内存占用。在PHP执行中,一旦根缓冲区满了或者调用gc_collect_cycles() 函数时,就会执行垃圾回收。

2、执行时间增加
垃圾回收影响性能的第二个领域它释放已泄漏的内存,耗费的时间。
通常,PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加。

但是在平常的(更小的)脚本中应根本就没有性能影响。

3、在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。因为总共使用的内存没达到上限。

这种好处在长时间运行脚本中尤其明显,诸如长时间的测试套件或者daemon脚本此类。

同时,对通常比Web脚本运行时间长的脚本应用程序,新的垃圾回收机制,应该会大大改变一直以来认为内存泄漏问题难以解决的看法。

补充问题:

PHP的GC有Full GC吗?

官方文档和网上资料并没有发现PHP 有Full GC一说。

会跟Java一样,当发生Full GC时,会导致整个进程/线程暂停/停顿?

从官方文档和网上资料来看,没有也不会。

5. 总结思考

PHP 的GC机制与Java的GC机制似有不同,还有其他语言的GC

需要后续研究。

后续补充

...

3.问题/补充

TBD

4.参考

PHP的垃圾回收机制以及大概实现 - 王召波 - 博客园   // PHP的垃圾回收机制以及大概实现  -- 推荐

PHP垃圾回收机制理解 - 张宇航 - 博客园   // PHP垃圾回收机制理解 -- 推荐

后续补充

...


网站公告

今日签到

点亮在社区的每一天
去签到