理解PHP的垃圾回收机制

什么是垃圾回收机制

使用的是“引用计数”方式进行回收。简单地理解的话,就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存。当指向该片内存的指针数量为0,那么该片内存区域就可以被回收。

什么又算垃圾

首先我们需要定义一下“垃圾”的概念, 说简单点是指变量的容器zval还存在,但是又没有任何变量名指向此zval。因此判断是否为垃圾的一个重要标准是有没有变量名指向变量容器zval。。

假设我们有一段PHP代码,使用了一个临时变量$tmp存储了一个字符串,在处理完字符串之后,就不需要这个$tmp变量了,$tmp变量对于我们来说可以算是一个“垃圾”了,但是$tmp其实并不是一个垃圾,

$tmp变量对我们没有意义,但是这个变量实际还存在,$tmp符号依然指向它所对应的zval,PHP代码中可能还会使用到此变量,所以不会将其定义为垃圾。

那么如果我们在PHP代码中使用完$tmp后,调用unset删除这个变量,那么$tmp是不是就成为一个垃圾了呢?先不着急回答这个问题。我们来举个例子:

<?php
$a = array(‘one‘);
$a[] = &$a;
unset($a);
?> 

这样$a数组就有两个元素,一个索引为0,值为字符one,另外一个索引为1,为$a自身的引用,内部存储如下:

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)=‘one‘,
   1 => (refcount=2, is_ref=1)=…
)

“…”表示1指向a自身,是一个环形引用:

最后我们对$a进行unset,那么$a会从符号表中删除,同时$a指向的zval的refcount减少1

那么问题也就产生了,$a已经不在符号表中了,用户无法再访问此变量,但是$a之前指向的zval的refcount变为1而不是0,因此不能被回收,这样产生了内存泄露:

这样,这么一个zval就成为了一个真是意义的垃圾了,新的GC要做的工作就是清理这种垃圾。

为解决这种垃圾,产生了新的GC

    在PHP5.3版本中,使用了专门GC机制清理垃圾,在之前的版本中是没有专门的GC,那么垃圾产生的时候,没有办法清理,内存就白白浪费掉了。在PHP5.3源代码中多了以下文件:{PHPSRC}/Zend/zend_gc.h {PHPSRC}/Zend/zend_gc.c, 这里就是新的GC的实现,我们先简单的介绍一下算法思路,然后再从源码的角度详细介绍引擎中如何实现这个算法的。

新的GC算法

    在较新的PHP手册中有简单的介绍新的GC使用的垃圾清理算法,这个算法名为 Concurrent Cycle Collection in Reference Counted Systems , 这里不详细介绍此算法,根据手册中的内容来先简单的介绍一下思路:

首先我们有几个基本的准则:

1:如果一个zval的refcount增加,那么此zval还在使用,不属于垃圾

2:如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾

3:如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾

只有在准则3下,GC才会把zval收集起来,然后通过新的算法来判断此zval是否为垃圾。那么如何判断这么一个变量是否为真正的垃圾呢?

简单的说,就是对此zval中的每个元素进行一次refcount减1操作,操作完成之后,如果zval的refcount=0,那么这个zval就是一个垃圾。

PHP5.3针对这个重大的缺陷做了优化。虽然其基础仍然是引用计数,但是在做了一些改良,能够将环状引用导致的内存泄露控制在一定的规模以内。当然,这并不是说你可以随便滥用内存,编写代码时仍然要小心为上!
补充重点:
1.PHP脚本运行完毕,该脚本申请的所有内存空间都会释放,不管是否存在环状引用。因此环状引用内存泄露的问题一般只影响长时间运行的程序脚本。
 
2.垃圾回收机制需要满足一定的条件才会执行。因此unset后,系统并不一定会立即回收垃圾。
 
3.unset的作用。
“unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1”。也就是说,如果有一个以上的变量指向同一个内存区域,或者存在环状引用,那么unset不会使内存区域释放。断开也说明unset并不会直接删除内存区域,而只是改变其引用计数而已。
 
4.$a=null的作用。
“$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0”。根据我对这个定义的理解,=null操作可以立即释放掉内存空间!

因此很多PHP技巧中不厌其烦地对我们说,先将变量设为null,再unset。理解其深层原理后,我才彻底理解了这样做的原因!=null才是根本!

原文地址:https://www.cnblogs.com/phpper/p/9220834.html

时间: 2024-10-30 21:03:49

理解PHP的垃圾回收机制的相关文章

理解Android Java垃圾回收机制

Jvm(Java虚拟机)内存模型 从Jvm内存模型中入手对于理解GC会有很大的帮助,不过这里只需要了解一个大概,说多了反而混淆视线. Jvm(Java虚拟机)主要管理两种类型内存:堆和非堆.堆是运行时数据区域,所有类实例和数组的内存均从此处分配.非堆是JVM留给自己用的,包含方法区.JVM内部处理或优化所需的内存(如 JIT Compiler,Just-in-time Compiler,即时编译后的代码缓存).每个类结构(如运行时常数池.字段和方法数据)以及方法和构造方法的代码. 简言之,Jav

简单理解python的垃圾回收机制

关键词:垃圾回收.引用计数.分代回收.标记-清除 前言:理解python中变量的定义:抽象理解python中变量的定义过程 1.垃圾回收机制的基本组成: python采用的是以引用计数为主,以分代回收和标记清除为辅的垃圾回收机制 2.详细分析垃圾回收机制: (1)首先是引用计数: 在python中,每创建一个对象,那么python解释器会自动为其设置一个特殊的变量,这个变量称为引用计数(初始值默认是1).一旦有一个新变量指向这个对象,那么这个引用计数的值就会加1.如果引用计数的值为0.那么pyt

03 JVM的垃圾回收机制

1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你应该知道一个技术名词:"stop-the-world" ,无论你选择哪种GC算法,"stop-the-world"都会发生."stop-the-world"意味着JVM停止应用程序,而去进行垃圾回收.当"stop-the-world&quo

PHP垃圾回收机制理解

使用的是"引用计数"方式进行回收.简单地理解的话,就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存.当指向该片内存的指针数量为0,那么该片内存区域就可以被回收. 引用计数计数简单,强大,但是有一个致命的缺陷,就是环状引用.考虑以下的代码: Php代码 $a = array(); $a[] = &$a; unset($a); 变量$a引用了自己,形成了一个环.$a被unset了,可是由于存在环状引用,因此$a之前指向的内存的引用计数为1,因此该内存区域不会被

【转】深入理解 Java 垃圾回收机制

深入理解 Java 垃圾回收机制 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 需要考虑内存管理.由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”.垃圾回收可以有效的防止内存泄露,有 效的使用空闲的内存. ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出

深入理解java垃圾回收机制

深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理.由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”.垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存. ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出

[Java]理解JVM之四:垃圾回收机制

JVM内存中的各个区域都会回收吗? 首先我们知道 Java 栈和本地方法栈在方法执行完成后对应的栈帧就立刻出栈销毁,两者的回收率可以认为是100%:Java 堆中的对象在没有被引用后,即使用完成后会被回收:方法区中的数据一般不会回收,只有在同时满足:所有实例被回收.加载该类的类加载器被回收.Class对象无法通过任何途径访问(包括反射)时才会回收:而程序计数器主要是记录指令执行的信息,在 HostSpot 虚拟机中是不会被回收的: 对于堆中的垃圾回收机制如下: 一.何时触发对象的回收 对象没有被

PHP垃圾回收机制的理解

PHP垃圾回收机制是php5之后才有的这个东西,下面我来给大家介绍一下关于PHP垃圾回收机制一些理解,希望对各位同学有所帮助. php 5.3之前使用的垃圾回收机制是单纯的"引用计数",也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器 1:当变量引用撤掉后,计数器-1:当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成. "引用计数"存在问题,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0:这时候,这

.NET垃圾回收机制的理解

网上的关于垃圾回收机制的帖子很多,这里我主要做个通俗易懂的总结 一.概念 简称GC,英文全称为Garbage Collector ,即垃圾收集器,在我的理解,也就是指系统内存中不再被使用且不需要再使用的对象,这些对象需要被回收 二.托管资源和非托管资源 托管资源:在托管堆上的资源,.net 80%都是托管资源 非托管资源:不在托管堆上的资源,常见的IO流对象,字符串连接对象等都是非托管资源 三.GC的作用域 GC主要回收托管资源,不能释放非托管资源,且GC并不是实时性的,非托管资源需手动释放 四