编程语言
首页 > 编程语言> > PHP中垃圾回收机制详解

PHP中垃圾回收机制详解

作者:互联网

前言

平时很多人说到的gc,就是垃圾回收器,全称Garbage Collection。

早期版本,准确地说是5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器的。只是简单的判断了一下变量的zval的refcount是否为0,是的话就释放否则不释放直至进程结束。

乍一看确实没毛病啊,然而其中隐藏着变量内存溢出的风险:http://bugs.php.net/bug.php?id=33595 ,无法回收的内存造成了内存泄漏,所以PHP5.3出现了专门负责清理垃圾数据、防止内存泄漏的GC。

php引用计数基本知识点

不准确但却通俗的说:
refcount:多少个变量是一样的用了相同的值,这个数值就是多少。
is_ref:bool类型,当refcount大于2的时候,其中一个变量用了地址&的形式进行赋值,好了,它就变成1了。

主要讲讲如何用php来直观的看到这些计数的变化,走一波。
首先需要在php上装上xdebug的扩展。

字符串类型变量

1.查看内部结构

<?php
    $name = "咖啡色的羊驼";
    xdebug_debug_zval('name');

会得到:

name:(refcount=1, is_ref=0),string '咖啡色的羊驼' (length=18)

2.增加一个计数

<?php
    $name = "咖啡色的羊驼";
    $temp_name = $name;
    xdebug_debug_zval('name');

会得到:

name:(refcount=2, is_ref=0),string '咖啡色的羊驼' (length=18)

看到了吧,refcount+1了。

3.引用赋值

<?php
    $name = "咖啡色的羊驼";
    $temp_name = &$name;
    xdebug_debug_zval('name');

  会得到:

name:(refcount=2, is_ref=1),string '咖啡色的羊驼' (length=18)

是的引用赋值会导致zval通过is_ref来标记是否存在引用的情况。

数组型的变量

<?php
    $name = ['a'=>'咖啡色', 'b'=>'的羊驼'];
    xdebug_debug_zval('name');

会得到:

name:
(refcount=1, is_ref=0),
array (size=2)
  'a' => (refcount=1, is_ref=0),string '咖啡色' (length=9)
  'b' => (refcount=1, is_ref=0),string '的羊驼' (length=9)

还挺好理解的,对于数组来看是一个整体,对于内部kv来看又是分别独立的整体,各自都维护着一套zval的refount和is_ref。

销毁变量

<?php
    $name = "咖啡色的羊驼";
    $temp_name = $name;
    xdebug_debug_zval('name');
    unset($temp_name);
    xdebug_debug_zval('name');

会得到:

name:(refcount=2, is_ref=0),string '咖啡色的羊驼' (length=18)
name:(refcount=1, is_ref=0),string '咖啡色的羊驼' (length=18)

refcount计数减1,说明unset并非一定会释放内存,当有两个变量指向的时候,并非会释放变量占用的内存,只是refcount减1.

php的内存管理机制

知道了zval是怎么一回事,接下来看看如何通过php直观看到内存管理的机制是怎么样的。

外在的内存变化

<?php
    //获取内存方法,加上true返回实际内存,不加则返回表现内存
    var_dump(memory_get_usage());
    $name = "咖啡色的羊驼";
    var_dump(memory_get_usage());
    unset($name);
    var_dump(memory_get_usage());

会得到:

int 1593248
int 1593384
int 1593248

大致过程:定义变量->内存增加->清除变量->内存恢复

潜在的内存变化

当执行 时候,内存的分配做了两件事情:

1.为变量名分配内存,存入符号表

2.为变量值分配内存

$name = "咖啡色的羊驼";

再来看代码:

<?php

    var_dump(memory_get_usage());
    for($i=0;$i<100;$i++)
    {
        $a = "test".$i;
        $$a = "hello";    
    }
    var_dump(memory_get_usage());
    for($i=0;$i<100;$i++)
    {
        $a = "test".$i;
         unset($$a);    
    }
    var_dump(memory_get_usage());

会得到:

int 1596864
int 1612080
int 1597680

简直爆炸,怎么和之前看的不一样?内存没有全部回收回来。

对于php的核心结构Hashtable来说,由于未知性,定义的时候不可能一次性分配足够多的内存块。所以初始化的时候只会分配一小块,等不够的时候在进行扩容,而Hashtable只扩容不减少,所以就出现了上述的情况:当存入100个变量的时候,符号表不够用了就进行一次扩容,当unset的时候只释放了”为变量值分配内存”,而“为变量名分配内存”是在符号表的,符号表并没有缩小,所以没收回来的内存是被符号表占去了。
 

潜在的内存申请与释放设计

php和c语言一样,也是需要进行申请内存的,只不过这些操作作者都封装到底层了,php使用者无感知而已。

php并非简单的向os申请内存,而是会申请一大块内存,把其中一部分分给申请者,这样当再有逻辑来申请内存的时候,就不需要向os申请了,避免了频繁调用。当内存不够的时候才会再次申请

当释放内存的时候,php并非会把内存还给os,而是把内存轨道自己维护的空闲内存列表,以便重复利用。

以上内容希望帮助到大家, 很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家 ,需要戳这里     PHP进阶架构师>>>实战视频、大厂面试文档免费获取    

 

标签:PHP,name,refcount,详解,垃圾,咖啡色,php,ref,内存
来源: https://www.cnblogs.com/plus666/p/14283629.html