php析构函数
析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。
析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。
php构造函数
具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用parent::__construct()。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。
php垃圾回收机制
引用计数:refcount /reference count
- 1. 如果引用计数减少到零,所在变量容器将被清除(free),不属于垃圾;
- 2. 如果一个zval的引用计数减少后还大于0,那么它会进入垃圾周期。
- 其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。
① 每个对象都内含一个引用计数器refcount,当内存对象被变量引用时,计数器+1;
② 当变量引用撤掉后(执行unset()后),reference离开生存空间或被设为 NULL,计数器-1;
③ 当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。
垃圾:变量的容器zval还存在,但是又没有任何变量名指向此zval。
因此GC判断是否为垃圾的一个重要标准是有没有变量名指向变量容器zval。
php变量在内存中存在于一个zval的一个变量容器中
容器结构如下:
is_ref bool值,表示变量是否有一个以上的别名。区分引用变量和普通变量
refcount 引用计数器 ,表示指向zval变量容器的变量个数。当refcount值为1时,is_ref的值为false。因为refcount为1,此变量不可能有多个别名,也就不存在引用了。当refcount=0时,zval可以垃圾回收
销毁对象
unset()
赋值
写时复制技术 cow copy on write
php5.3中将一个变量 = 赋值给另一个变量时,不会立即为新变量分配内存空间,而是在原变量的zval中给refcount加1。 只有当原变量或者发生改变时,才会为新变量分配内存空间,同时原变量的refcount减 1 。
当然,如果unset原变量,新变量直接就使用原变量的zval而不是重新分配。
&引用赋值时,原变量的is_ref 加1. 如果给一个变量&赋值,之前 = 赋值的变量会分配空间。
$a=array('one');
$a[]=&$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,因此不能被回收,这样产生了内存泄露。
程序运行完才能回收
回收周期
默认的,PHP的垃圾回收机制是打开的,然后有个php.ini设置允许你修改它:zend.enable_gc 。
除了修改配置zend.enable_gc ,也能通过分别调用gc_enable() 和 gc_disable()函数在运行php时来打开和关闭垃圾回收机制。
1.节省内存空间
2.增加执行时间,仅在循环回收算法上增加时间,小脚本可忽略不计
PHP7的zval和PHP5.x相比有什么改变
1.PHP5标量数据类型会计数,php7看到标量(布尔,字符串,整形,浮点型)不再计数,不需要单独分配内存
2.php5 数组用了比数组长度多1个zval存储。php7 数组的refcount都是从2开始的。PHP7的复杂数据类型(比如数组和对象)的引用计数由其自身来存储。
3.PHP7的zval 需要的内存不再是单独从堆上分配,不再自己存储引用计数。
PHP5.3的垃圾回收算法有以下几点特性:
1、并不是每次refcount减少时都进入回收周期,只有根缓冲区满额后在开始垃圾回收。
2、可以解决循环引用问题。
3、可以总将内存泄露保持在一个阈值以下。
每一个变量对应一个 zval 数据结构,在该结构内还有一个 val 结构体,该结构体内有一个引用计数(php7 而言,对于 php5,这个引用计数是保存在 zval 结构中的),标识该对象的引用数,当对象的引用计数为 0 时代表这个对象可被回收。
对象的 refcount 减少的时机:修改变量、函数返回(释放局部变量)、unset 变量
对于数组和对象而言,可能存在变量中的成员引用变量本身的情况,也就是循环引用,这样会造成这个变量永远不会被内存回收,而成为垃圾。
PHP 里对于这种情况给出了垃圾回收机制:如果数组、对象的引用计数减少而且不为零,则认为他们可能是垃圾,把他们放到垃圾收集器里。等垃圾收集器到了一定的数量之后,进行垃圾处理:对所有可能的垃圾 refcount 减 1,如果为 1,说明是垃圾,则进行内存回收;如果不为 1,说明还有其他变量在使用,refcount 重新加 1;这种对象复用以及垃圾回收机制在其他语言中也有体现:redis 中也使用了引用计数表示每个对象的引用数量。