• PHP常驻进程,内存泄漏排查指南


    1、初步定为泄漏:

    • 迫于 996ICU 的压力,广大的 PHPer 一般不会关注泄漏问题,都是在看到报错
    Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 12288 bytes)

    才发现泄漏问题,此时我们一般会通过查看进程的RSS占用来确定内存占用,例如这样

    cat /proc/28806/status |grep RSS
    一定要注意的是,此处查看的RSS是包含共享内存的(共享的内存会重复计算多次),并不是进程真正占用的内存,USS才是我们 PHP 代码申请的内存,我们更应该关注的是USS指标。(感兴趣的小伙伴可以看这个视频)。
    • 怎么看USS,这里推荐smem这个命令,用法和结果如下:

    RSS 占用特别多,USS 特别少,证明大部分 RSS 都是共享内存占用,此时大概率是你的Swoole Table或者Apcu用的有问题,因为这两个底层是基于共享内存的。

     

    2、定位泄漏的代码:

    可以用Swoole Tracker提供的工具来定位,具体参考这篇文章

     

    3、清理内存碎片:

    如果 Tracker 发现不了泄漏,内存还一直涨,八成是遇到了 PHP 的内存碎片问题,内存碎片问题也是我想写这篇文章的原因,社区里面有个小伙伴用了Swoole Tracker没有发现泄漏,但是通过 smem 命令查看内存确实在涨,即使unset了所有变量,内存仍然无法降下去,代码如下:

    function main()
    {
        for ($i = 1; $i < 2000000; $i++) {
            $GLOBALS[$i] = str_repeat("str_repeat这个函数会申请内存,但我马上就unset掉", 10);
        }
        for ($i = 1; $i < 2000000; $i++) {
            unset($GLOBALS[$i]);
        }
    }
    main();

    咋回事呢?根本原因是产生了内存碎片,和 PHP 的内存分配算法有关,这里不展开讲,大概原理是:小于 3072 字节的内存申请 PHP 会认为是小内存,PHP 会把所有申请的小内存块缓存起来,即使释放了也不归还给操作系统,以保证内存管理的效率

    在 FPM 下请求结束后会释放所有内存,大部分归还给系统,只保留一小部分,但是在 Cli 下没有这样的机制,我们该怎么办?

    我研究了一天,貌似只能给 php-src 提 pr 了,写了一天发现"咦?"怎么有段代码怎么和我的思路这么相似,仔细一看"日哦",在 php 高版本提供了一个gc_mem_caches()函数(网上没有任何文章介绍这个函数),可以手动清理这种小内存的碎片问题 - -!

    我们可以设置一个 Swoole 的定时器,定期调用gc_mem_caches()即可。

    除了gc_mem_caches()我们也可以通过扩展替换 PHP 的内存管理模块(比如采用 jemalloc)来避免这种问题,注意所有的内存管理算法都有内存碎片的问题,需要当心。

    总结

    第一步正确的发现泄漏,第二步使用 tracker 定位泄漏代码并 fix 它,第三步如果还是泄漏,尝试调用gc_mem_caches()清理碎片。

  • 相关阅读:
    SAP资产变动明细
    SAP资产明细报表
    SAP连接HANA数据库
    工程变更记录报表
    SELECTION-SCREEN 文本丢失
    FG函数模块
    DOI EXCEL显示报表
    OLE填充EXCEL
    下载模板,上传EXCEL
    SELECTION-SCREEN 加按钮
  • 原文地址:https://www.cnblogs.com/zgxblog/p/14310152.html
Copyright © 2020-2023  润新知