danxill / php-meminfo-analyzer
用于分析meminfo转储文件的工具,它是bitone/mem-info项目的一部分
Requires
- clue/graph: ^0.9.0
- graphp/algorithms: ^0.8.1
- symfony/console: ^3.4 || ^4.4 || ^5.0
- symfony/filesystem: ^3.4 || ^4.4 || ^5.0
- symfony/serializer: ^3.4 || ^4.4 || ^5.0
Requires (Dev)
- phpspec/phpspec: ^4.3 || ^5.1 || ^6.3 || ^7.0
README
该存储库使用来自https://github.com/BitOne/php-meminfo的一些代码来简化集成
使用方法
转储内存内容
meminfo_dump(fopen('/tmp/my_dump_file.json', 'w'));
此函数以JSON格式生成PHP内存的转储。此转储可以由提供的分析器稍后分析。
此函数接受一个流句柄作为参数。它允许您指定一个文件(例如fopen('/tmp/file.txt', 'w')
),以及使用php://stdout
流使用标准输出。
显示内存中项目的摘要
$ bin/analyzer summary <dump-file> Arguments: dump-file PHP Meminfo Dump File in JSON format
示例
$ bin/analyzer summary /tmp/my_dump_file.json +----------+-----------------+-----------------------------+ | Type | Instances Count | Cumulated Self Size (bytes) | +----------+-----------------+-----------------------------+ | string | 132 | 7079 | | MyClassA | 100 | 7200 | | array | 10 | 720 | | integer | 5 | 80 | | float | 2 | 32 | | null | 1 | 16 | +----------+-----------------+-----------------------------+
显示具有最大子项数量的对象列表
$ bin/analyzer top-children [options] [--] <dump-file> Arguments: dump-file PHP Meminfo Dump File in JSON format Options: -l, --limit[=LIMIT] limit [default: 5]
示例
$ bin/analyzer top-children /tmp/my_dump_file.json +-----+----------------+----------+ | Num | Item ids | Children | +-----+----------------+----------+ | 1 | 0x7ffff4e22fe0 | 1000000 | | 2 | 0x7fffe780e5c8 | 11606 | | 3 | 0x7fffe9714ef0 | 11602 | | 4 | 0x7fffeab63ca0 | 3605 | | 5 | 0x7fffd3161400 | 2400 | +-----+----------------+----------+
查询内存转储以查找特定对象
$ bin/analyzer query [options] [--] <dump-file> Arguments: dump-file PHP Meminfo Dump File in JSON format Options: -f, --filters=FILTERS Filter on an attribute. Operators: =, ~. Example: class~User (multiple values allowed) -l, --limit=LIMIT Number of results limit (default 10). -v Increase the verbosity
示例
$ bin/analyzer query -v -f "class=MyClassA" -f "is_root=0" /tmp/php_mem_dump.json +----------------+-------------------+------------------------------+ | Item ids | Item data | Children | +----------------+-------------------+------------------------------+ | 0x7f94a1877008 | Type: object | myObjectName: 0x7f94a185cca0 | | | Class: MyClassA | | | | Object Handle: 1 | | | | Size: 72 B | | | | Is root: No | | +----------------+-------------------+------------------------------+ | 0x7f94a1877028 | Type: object | myObjectName: 0x7f94a185cde0 | | | Class: MyClassA | | | | Object Handle: 2 | | | | Size: 72 B | | | | Is root: No | | +----------------+-------------------+------------------------------+ | 0x7f94a1877048 | Type: object | myObjectName: 0x7f94a185cf20 | | | Class: MyClassA | | ...
显示引用路径
引用路径是内存中特定项(通过其指针地址标识)与所有中间项之间的路径,直到连接到程序中仍存活变量的项。
此路径显示哪些项负责特定项的内存泄漏。
$ bin/analyzer ref-path <item-id> <dump-file> Arguments: item-id Item Id in 0xaaaaaaaa format dump-file PHP Meminfo Dump File in JSON format Options: -v Increase the verbosity
示例
$ bin/analyzer ref-path -v 0x7f94a1877068 /tmp/php_mem_dump.json Found 1 paths Path from 0x7f94a1856260 +--------------------+ | Id: 0x7f94a1877068 | | Type: object | | Class: MyClassA | | Object Handle: 4 | | Size: 72 B | | Is root: No | | Children count: 1 | +--------------------+ ^ | 3 | | +---------------------+ | Id: 0x7f94a185cb60 | | Type: array | | Size: 72 B | | Is root: No | | Children count: 100 | +---------------------+ ^ | second level | | +--------------------+ | Id: 0x7f94a185ca20 | | Type: array | | Size: 72 B | | Is root: No | | Children count: 1 | +--------------------+ ^ | first level | | +---------------------------+ | Id: 0x7f94a1856260 | | Type: array | | Size: 72 B | | Is root: Yes | | Execution Frame: <GLOBAL> | | Symbol Name: myRootArray | | Children count: 1 | +---------------------------+
使用PHP Meminfo查找和理解内存泄漏的工作流程
PHP的其他内存调试工具
-
XDebug (https://xdebug.org/) 使用跟踪功能和内存增量选项(请参阅XDebug文档),您可以跟踪函数内存使用情况。您可以使用提供的脚本来获取汇总视图(待办事项:链接)
-
PHP Memprof (https://github.com/arnaud-lb/php-memory-profiler) 提供关于函数内存使用的汇总数据。比XDebug的完整跟踪资源消耗要少。
故障排除
"由memory_usage
条目报告了大量内存使用,但摘要中项目的累积大小远低于内存使用量"
Zend引擎本身内部使用了大量内存来编译PHP文件、运行虚拟机、执行垃圾收集器等... 另一部分内存通常由PHP扩展本身占用。剩余的内存使用量来自您程序中的PHP数据结构。
在某些情况下,某些PHP扩展可能会内部使用几百兆字节。例如PDO扩展和MySQLi扩展。默认情况下,当执行SQL查询时,它们将所有结果缓冲在PHP内存中:https://php.ac.cn/manual/en/mysqlinfo.concepts.buffering.php
如果结果数量非常多,这将消耗大量内存,而这种内存使用量并非由您程序中的对象或数组的数据引起,而是由扩展的工作方式引起的。
这是一个例子,但类似的情况也可能发生在图像处理扩展上,这些扩展将使用大量内存来转换图像。
所有扩展都使用Zend内存管理器,因此它们不会超过为PHP进程设置的内存限制。因此,它们的内存使用量包含在memory_get_usage()
提供的信息中。
但PHP Meminfo只能获取PHP程序中数据结构的内存使用信息,而不是来自扩展本身。
因此,这些数字之间的差异可能相当大。
调用meminfo_dump
时出现"调用未定义的函数"错误
这意味着扩展尚未启用。
检查 PHP 信息输出的 MemInfo 数据。
要查看 PHP 信息输出,只需创建一个调用 phpinfo();
函数的页面,然后从浏览器中加载它,或者从命令行调用 php -i
。
为什么大多数测试都是“跳过”的?
在执行 make test
时,一些测试将需要 JSON 功能。但是编译系统通过删除所有加载扩展的配置指令来生成一个干净的 env。因此,如果 JSON 功能被打包为一个单独的扩展(而不是直接编译到 PHP 运行时中),则测试将被跳过。
您可以使用 make test
命令之后生成的 run-tests.php
运行它们,通过提供 php
可执行文件。
$ TEST_PHP_EXECUTABLE=$(which php) $(which php) run-tests.php -d extension=$PWD/modules/meminfo.so
在这种情况下,您的测试将使用您本地的 PHP 配置运行,包括加载 JSON 扩展。
请注意,当使用 PHP 8 时,这不是必需的,因为 JSON 函数现在通常直接编译到 PHP 中。
致谢
感谢 Derick Rethans 对关键的 XDebug 的启发式工作。请参阅 http://www.xdebug.org/