PHP 中如何定位内存泄露的代码所在行呢?

2020-08-13 18:20:47 +08:00
 JJstyle

phper 们应该都会自动内存泄露主要由于循环应用导致的,比如如下代码将会导致内存泄露:

function fun1()
{
  $a = ['hello'];
  $a[] = &$a;
}

func1();

自己的代码还好,可以检查检查,由于使用了第三方依赖,怎么在依赖的文件中定位到有这种有内存泄露的代码呢?小弟实在想不出有什么好的办法

4014 次点击
所在节点    PHP
18 条回复
leven87
2020-08-13 18:41:01 +08:00
memory_get_usage ? 不懂
知名一些的第三方应用,都有 github 或者论坛吧。
sagaxu
2020-08-13 18:43:58 +08:00
循环引用不会导致内存泄露
JJstyle
2020-08-13 19:06:53 +08:00
@sagaxu 你可以执行我上面的那段代码试试内存使用
ben1024
2020-08-13 19:19:09 +08:00
xdebug 监听函数 memory_get_usage(true)/1024/1024/8 的内存值?
wangritian
2020-08-13 19:28:11 +08:00
apache/php-fpm/swoole 应该都有一个叫 max_request 的参数,执行一定量请求后重启进程,哪怕代码有内存泄漏也会还给系统
cholerae
2020-08-13 19:44:03 +08:00
不懂 php,不过搜了一下看 php 5.3 开始就有能回收环的 gc 了,为啥这个代码会内存泄漏?
JJstyle
2020-08-13 19:45:29 +08:00
@wangritian 是一段脚本,在 cli 下运行的,其实我只是讨论一个方法,毕竟我可以发现当内存达到一定量时退出,rnhou 让 supervisor 帮我重启
JJstyle
2020-08-13 19:46:11 +08:00
@JJstyle #7 rnhou => 然后
JJstyle
2020-08-13 20:09:42 +08:00
@cholerae 我看了一下 php 的文档,大概是 php5.3 后,只有当根缓冲区满了才会 清除循环引用产生的垃圾
superwhite
2020-08-13 20:21:06 +08:00
php7.2 win,PHP7.3 centOs,试了你的代码,不会内存泄露
CismonX
2020-08-13 21:31:06 +08:00
在 PHP 的 debug build 中,PHP 进程退出时 Zend MM 会打印出未释放的内存的相关信息(仅限那些用 emalloc 系列函数分配的由 Zend MM 管理的内存),包括其地址、分配这块内存的相关函数等

当然,实际调试的时候这些信息可能不够,需要 valgrind 。
wangbenjun5
2020-08-13 21:33:01 +08:00
@wangritian 正解,除非写常驻进程的应用,不然不用操心内存泄露问题,如果要写常驻进程应用,为何不用 go
JJstyle
2020-08-14 01:30:39 +08:00
@wangbenjun5 帮朋友维护一个个人项目,恰好我对 php 熟悉
zjsxwc
2020-08-14 08:23:09 +08:00
看来只能手动 gc 了

1 <?php
2
3
4 function fun1()
5 {
6 $a = ['hello'];
7 $a[] = &$a;
8 }
9
10 while(true) {
11 fun1();
12 gc_collect_cycles();
13 echo memory_get_usage() . "\n";
14 sleep(1);
15 }
zjsxwc
2020-08-14 08:40:24 +08:00
php 碰到内存“泄露”问题,有几个方面
1. “全局变量” 或者 “常驻依赖注入容器对象” 的膨胀导致内存不够,比如 orm 中 EntityManager 没有及时 clear 追踪的不再被使用的数据库实体对象。
2. 代码逻辑“死循环” 导致内存不够。
3. 使用了存在内存泄露 bug 的 c/c++拓展。


当接楼上我的回答,当 php 内存达到设置值时,比如 32M 时会自动触发垃圾回收,所以没有必要手动 gc_collect_cycles();


php 7.3 之后有个 gc_status();可以看 gc 状态


```
zjsxwc@zjsxwc-PC:~$ cat 2.php
<?php


function fun1()
{
$a = ['hello'];
$a[] = &$a;
}

while(true) {
var_dump(gc_status());
fun1();
var_dump(gc_status());
gc_collect_cycles();
echo memory_get_usage() . "\n";
sleep(1);
}
zjsxwc@zjsxwc-PC:~$ '/home/zjsxwc/php74/bin/php' 2.php
array(4) {
["runs"]=>
int(0)
["collected"]=>
int(0)
["threshold"]=>
int(10001)
["roots"]=>
int(0)
}
array(4) {
["runs"]=>
int(0)
["collected"]=>
int(0)
["threshold"]=>
int(10001)
["roots"]=>
int(1)
}
365496
array(4) {
["runs"]=>
int(1)
["collected"]=>
int(1)
["threshold"]=>
int(10001)
["roots"]=>
int(0)
}
array(4) {
["runs"]=>
int(1)
["collected"]=>
int(1)
["threshold"]=>
int(10001)
["roots"]=>
int(1)
}
365496

```
wangritian
2020-08-14 09:34:53 +08:00
@wangbenjun5 +1 我也是 web 项目用 php-swoole,小工具或者中间件用 go/python
gouchaoer2
2020-08-14 11:08:03 +08:00
php 有检测循环引用的机制,只是内存涨到一定大小才会触发
jimduan
2020-08-14 18:19:31 +08:00
php5.3 后的 gc, 已经自动帮我们解决了!
只需要更多的关注, 业务场景中是否一次性拉取过多的数据进行计算导致 OOM ;常驻内存脚本是否请求了很多资源没有释放


PHP 的垃圾回收机制-理解 PHP 如何解决循环引用导致的内存泄漏问题
http://blog.100dos.com/2017/04/07/php-garbage-collection-collect-cycles/

请手动释放你的资源(Please release resources manually)
https://www.laruence.com/2012/07/25/2662.html

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/698052

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX