PHP 中如何定位内存泄露的代码所在行呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JJstyle
V2EX    PHP

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

  •  
  •   JJstyle 2020-08-13 18:20:47 +08:00 4461 次点击
    这是一个创建于 1932 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    第 1 条附言    2020-08-14 01:28:06 +08:00

    上面的示例代码不是特别好,请看下面:

    <?php function fun1() { $a = ['hello']; $a[] = &$a; } while(true) { fun1(); echo memory_get_usage() . "\n"; sleep(1); } 

    有兴趣的朋友可以试试上面的代码,内存将会不断增加(centos7 php7.4)

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

    当然,实际调试的时候这些信息可能不够,需要 valgrind 。
    wangbenjun5
        12
    wangbenjun5  
       2020-08-13 21:33:01 +08:00
    @wangritian 正解,除非写常驻进程的应用,不然不用操心内存泄露问题,如果要写常驻进程应用,为何不用 go
    JJstyle
        13
    JJstyle  
    OP
       2020-08-14 01:30:39 +08:00
    @wangbenjun5 帮朋友维护一个个人项目,恰好我对 php 熟悉
    zjsxwc
        14
    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
        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
        16
    wangritian  
       2020-08-14 09:34:53 +08:00
    @wangbenjun5 +1 我也是 web 项目用 php-swoole,小工具或者中间件用 go/python
    gouchaoer2
        17
    gouchaoer2  
       2020-08-14 11:08:03 +08:00
    php 有检测循环引用的机制,只是内存涨到一定大小才会触发
    jimduan
        18
    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
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4517 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 05:37 PVG 13:37 LAX 21:37 JFK 00:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86