引用赋值$a='apple';$b=&$a;在上面的代码中,我将一个字符串赋值给了变量a,然后赋值给了一个引用变量b。显然,此时的内存指针应该是这样的:$a->'apple'<-$ba和b指向同一个内存区(变量容器zval),我们得到string(5)"apple"string(5)"apple",这是我们预期的结果。unset函数和引用计数unset函数如果我想从内存中释放字符串“apple”。我这样做了:unset($a);但是通过再次打印$a$b这两个变量的信息,我得到了这个结果:Notice:Undefinedvariable:aandstring(5)"apple"。奇怪,$a$b指向同一个变量容器,而$a明明是释放了,为什么$b还是'apple'。其实unset()只是销毁了一个变量符号a(指针),并没有释放变量容器,所以操作完成后,内存指针就变成了这样:'apple'<-$breferenceCountthereferencecount(引用计数)是存储在每个变量容器中的一条信息,表示当前变量容器被多少个变量符号引用。和前面的例子一样,unset()并没有释放变量指向的变量容器,只是销毁了变量符号。同时,将变量容器中的引用计数减1。当引用计数为0时,即变量容器没有被任何变量引用时,会触发php垃圾回收(错误),并且它将被释放(正确)。更正上面的一个小错误:这种简单的引用计数方式是php5.2之前的内存管理机制,不能称之为垃圾回收机制。PHP5.3引入了垃圾回收机制。垃圾回收机制就是为了解决这个问题。简单的引用计数内存管理机制的缺陷(即循环引用造成的内存泄漏,下面会解释)回到正题,我们用代码来验证前面的结论:$a='apple';$b=&$a;$before=memory_get_usage();unset($a);$after=memory_get_usage();var_dump($before-$after);//结果为int(0),变量容器的引用计数为1,不释放$a='apple';$b=&$a;$before=memory_get_usage();unset($a,$b);$after=memory_get_usage();var_dump($before-$after);//result为int(24),变量容器的引用计数为0,释放则直接释放,那我们怎么才能真正释放'apple'占用的内存呢?使用上面的方法,我们可以在unset($a)之后unset($b)销毁变量容器的所有引用,引用计数减为0,自然释放。当然还有更直接的方法:$a=null;直接赋值null会清空$a指向的内存区域,将引用计数清零,内存就会被释放。脚本执行后的内存对于一般的web程序(fpm方式),PHP的执行是单线程同步阻塞式的。当脚本执行结束时,脚本中使用的所有内存将被释放。那么,我们手动释放内存有意义吗?其实这个问题早就有了答案。推荐大家看一下鸟哥@laruence2012年发表的一篇文章:Pleasereleaseyourresourcesmanually(请手动释放资源)。说说前面提到的引用计数内存管理机制的缺陷。当变量容器的引用计数为0时,PHP将进行垃圾回收。但是,你可以想象有一种情况会导致一个变量容器的引用计数永远不会减为0。例如:$a=['one'];$a[]=&$a;让我们看看现在,$a数组的第二个元素是它本身。那么,存放数组的变量容器的引用计数为2,一个引用是变量a,另一个引用是数组的第二个元素——索引1。那么,如果我们此时unset($a),存储数组的变量容器的引用计数会减1,但是还有一个引用,就是数组的第1个元素,现在引用结构变成了这样:由于变量容器的引用count还没有变成0,所以不能释放,此时没有其他外部变量符号可以引用它,用户也没有办法清除这个结构,那么它就会一直驻留在内存中。所以如果代码中有很多这样的结构和操作,最终会导致内存丢失甚至泄漏。这就是循环引用导致的内存无法释放的问题。幸运的是,在fpm模式下,当请求的脚本执行结束时,php会释放所有脚本使用的内存,包括这个结构体。但是,如果是守护进程下的php程序呢?比如swoole。这是PHP急需解决的问题(已经解决,见下文)。PHP5.3.0引入的同步算法传统上,像以前PHP使用的引用计数内存机制一样,无法处理循环引用导致的内存泄漏。不过PHP5.3.0使用了文章?ConcurrentCycleCollectioninReferenceCountedSystems(引用计数系统中的并发循环收集)中的同步算法来解决这个内存泄漏问题。这个算法就是PHP的垃圾回收机制。具体算法的实现和过程有点复杂,请阅读官方文档,这里不再赘述,附上几个讲解算法过程的文章链接,比较直白:http://php。net/manual/zh/feat...官方文档http://www.cnblogs.com/leoo2s...https://blog.csdn.net/phpkern...最后还是参考这两段鸟哥的文章说明了问题:在PHP5.2之前,PHP使用引用计数(Referencecount)进行资源管理。当一个zval的引用计数为0时,它就会被释放。虽然有循环引用(Cyclereference),但是这样的设计对于开发非常重要的web脚本来说是没有问题的,因为web脚本的特点和他们的目标都是执行时间短,不会运行很长时间。对于循环引用造成的资源泄漏,会在请求结束时释放。也就是说,当请求结束时是一种补救措施(备份)。但是随着PHP被越来越多的人使用,很多人在一些后台脚本中使用了PHP。这些脚本的特点是长时间运行,如果出现循环引用,导致引用计数无法及时释放不用的资源,脚本最终会耗尽内存退出。所以在PHP5.3之后,我们引入了GC,也就是说我们引入GC来解决用户无法解决的问题。
