最近工作上需要排查 php 频繁达到内存限制进程被杀掉的原因。项目中使用php写一个死循环,把 mysql 的数据同步到mq或者 mongodb 当中。内存问题主要出现在mq消息的发布上。项目中有使用到php-amqplib。
跟踪代码发现,循环内部,获取mq单例对象有问题导致每次循环都是new的一个mq对象。刚开始以为是这个原因导致内存不断增长。三下五除二就改完了,结果一试,没什么效果,还是不断飙升啊。
既然不是新对象引起的,那估计就是就对象的问题。因为新建对象都没有对已有的mq对象进行处理,例如端口连接,释放资源等。因此在新建对象之前,执行php-amqplib 中connection的close操作,关闭连接以及释放资源。关闭之后再操作,确实有些改变,飚的慢点,但是还是会飚。然后又在循环结束的时候unset对象,结果依然没什么变化。
只能接着看代码。php-amqplib中connection的属性中有一个channels属性,用于保存channel对象数组。然而这个channel对象本身又有一个connection属性,这样这两个对象之间就构成一个循环引用,当我们删除connection以及channel的时候,内部引用计数器不会到0,所以内存不会被释放。
用一下简化版说明一下其中的问题:
按正常的逻辑,对象赋值null,那对象所占用的内存应该要被释放。上面的代码输出内容如下:
可以看出,跑了4500次之后内存就已经操作128M了。如果Channel中没有connection的属性,则会有不一样的结果。我们把Channel的构造方法注释掉,再重新跑
只是一个简单的修改,循环就没有内存的问题了。
问题的根本就是对象之间循环引用。有个很有趣的现象,如果对象之间构成循环引用,在xdebug中就可以看到一个无限的树状对象。Connection->Channel->Connection->Channel....
对于普通的web应用而已,一般不会有什么问题,每次请求结束之后fpm会释放掉。但是对于cli应用,这就是致命的。基本上跑个一天就挂了。
但是,现实就是这样。对象之间相互引用很容易出现。这个model需要那个model,几个model之间也很容易构成一个回环。同时,很多东西需要引用第三方类,没办保证第三方类没有相互引用。那有没有不改类之间引用可以解决的呢?
在这次排查,我使用的是 gc_collect_cycles()
强制执行gc操作,释放内存。还是第一段程序代码,循环内容改为一下内容:
输出内容如下:
内存飙升的问题解决了。
网上很多描述都是php5.3之后的gc会自动回收类似这类的垃圾,但是前提是zend节点满了。但实际上,说的只是数组类型。下面的代码在循环结束之后,局部变量data的资源会得到释放。
总的而言,PHP在一些长时间的循环运行当中,一定要小心对象之间相互引用造成内存上升的问题。如果遇到内存上升问题,可以先看看代码当中有没有什么类之间存在循环引用。平时写代码的时候也需要尽量避免对象之间构成循环引用,避免在不经意之间给自己或团队挖个坑。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 不可不知的 Spark shuffle
- 不可不知的spark shuffle
- 真的,你不可不知的 Java 反射
- 那些你不得不知的抢购业务要点
- 不可不知的 ZooKeeper 小工具 zkui
- 你所不知的 PHP 断言(assert)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
无线:网络文化中激进的经验主义
[英] 阿德里安·麦肯齐 / 张帆 / 上海译文出版社 / 2018-9
本书研究了无线是如何成为当代人类经验的主角的。从路由器、智能电话、电子书、城市到在线工作、服务协议、玩具以及国家等各个方面,人们已经感觉到了无线技术所引发的变革。本书作者援引一个世纪之前的哲学技术来分析当代最前沿的后网络时代的人类状况。基于威廉•詹姆斯的实用主义哲学相关的彻底经验主义,作者提出了把失序的无线网络世界与人们的感知匹配起来的新方式。一起来看看 《无线:网络文化中激进的经验主义》 这本书的介绍吧!