如何确定变量的内存占用(大小)?

Pis*_*3.0 101 php memory

PHP(或PHP扩展)中是否有一个函数可以找出给定变量使用了多少内存?sizeof只是告诉我元素/属性的数量.

memory_get_usage有助于它给我整个脚本使用的内存大小.有没有办法为单个变量执行此操作?

请注意,这是在开发机器上,因此加载扩展或调试工具是可行的.

Tat*_*nen 92

没有直接的方法来获取单个变量的内存使用情况,但正如Gordon建议的那样,你可以使用memory_get_usage.这将返回分配的内存总量,因此您可以使用变通方法并测量前后使用情况以获取单个变量的使用情况.这有点hacky,但它应该工作.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;
Run Code Online (Sandbox Code Playgroud)

请注意,这绝不是一种可靠的方法,您无法确定在分配变量时没有其他任何内容触及内存,因此这只应用作近似值.

实际上,您可以通过在函数内创建变量的副本并测量使用的内存来将其转换为函数.没有测试过这个,但原则上我没有看到它有什么问题:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}
Run Code Online (Sandbox Code Playgroud)

  • `$ tmp = $ var`将创建一个浅表副本.在修改$ tmp之前,这不会分配更多内存. (14认同)
  • 怎么样`$ tmp = unserialize(serialize($ var))`; 这将结合Aistina的方法. (7认同)
  • 另外,由于`$ var`已经是传递给函数的浅层副本或引用,你不需要`$ tmp`,但可以重新分配给`$ var`.这将内部引用从`$ tmp`保存到`$ var`. (3认同)

Vin*_*982 44

您可能需要Memory Profiler.我收集了信息,但是我复制了一些可能对你有帮助的重要事情.

您可能知道,自从2.*版本以来,Xdebug删除了内存分析支持.请在此处搜索"已删除的函数"字符串:http://www.xdebug.org/updates.php

删除了功能

删除了对内存分析的支持,因为它无法正常工作.

其他Profiler选项

PHP-存储器廓

https://github.com/arnaud-lb/php-memory-profiler.这是我在Ubuntu服务器上完成的启用它:

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart
Run Code Online (Sandbox Code Playgroud)

然后在我的代码中:

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));
Run Code Online (Sandbox Code Playgroud)

最后callgrind.outKCachegrind打开文件

使用谷歌gperftools(推荐!)

首先在此处下载最新的软件包来安装Google gperftools:https://code.google.com/p/gperftools/

然后一如既往:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install
Run Code Online (Sandbox Code Playgroud)

现在在您的代码中:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));
Run Code Online (Sandbox Code Playgroud)

然后打开终端并启动:

pprof --web /tmp/profile.heap
Run Code Online (Sandbox Code Playgroud)

pprof将在您现有的浏览器会话中创建一个新窗口,如下所示:

使用memprof和gperftools进行PHP内存分析

Xhprof + Xhgui(在我看来,最好的配置cpu和内存)

使用XhprofXhgui,您可以分析cpu的使用情况,或者只是内存使用情况,如果这是您目前的问题.这是一个非常完整的解决方案,它可以让您完全控制,并且可以在mongo或文件系统中编写日志.

欲了解更多详细信息,请参见这里.

黑火

Blackfire是SensioLabs的PHP探查器,Symfony2人员https://blackfire.io/

如果您使用puphpet设置您的虚拟机,您会很高兴知道它的支持;-)

Xdebug和跟踪内存使用情况

XDEBUG2是PHP的扩展.Xdebug允许您记录所有函数调用,包括参数和返回值到不同格式的文件.有三种输出格式.一个是人类可读的跟踪,另一个更适合计算机程序,因为它更容易解析,最后一个使用HTML格式化跟踪.您可以使用该设置在两种不同格式之间切换.这里有一个例子

forp

forp简单,非侵入式,面向生产,PHP分析器.一些功能是:

  • 测量每个功能的时间和分配的内存

  • CPU使用率

  • 函数调用的文件和行号

  • 输出为Google的跟踪事件格式

  • 功能标题

  • 功能分组

  • 函数别名(对匿名函数有用)

DBG

DBG是一个功能齐全的php调试器,一个可以帮助您调试PHP脚本的交互式工具.它适用于生产和/或开发WEB服务器,允许您从IDE或控制台本地或远程调试脚本,其功能包括:

  • 远程和本地调试

  • 显式和隐式激活

  • 调用堆栈,包括函数调用,动态和静态方法调用及其参数

  • 通过调用堆栈导航,能够评估相应(嵌套)位置中的变量

  • 步入/退出/跳过/运行到光标功能

  • 条件断点

  • 全球断点

  • 记录错误和警告

  • 并行调试的多个同时会话

  • 支持GUI和CLI前端

  • 支持IPv6和IPv4网络

  • 调试器传输的所有数据都可以选择使用SSL进行保护

  • 这正是*我正在寻找的信息,谢谢. (2认同)

Ais*_*ina 24

不,那里没有.但你可以serialize($var)检查strlen结果的近似值.

  • 这是一个可怕的近似.PHP中数组中的每个项目都是~80个字节,但是`strlen(serialize(array(1,2,3)))`是30. (12认同)
  • 至少相对大小的良好启发式. (3认同)
  • @Aistina,-1.你在测量错误的东西.变量和序列化变量是两个*完全*不同的东西,并将给出完全不同的结果. (2认同)

par*_*ara 20

回答Tatu Ulmanens的回答:

应该注意,它$start_memory本身将占用内存(PHP_INT_SIZE * 8).

所以整个功能应该变成:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}
Run Code Online (Sandbox Code Playgroud)

很抱歉将此作为额外答案添加,但我还不能评论答案.

更新:*8不是确定的.它显然可能依赖于php版本,可能还有64/32位.

  • 你能解释为什么`*8`?谢谢! (4认同)

Ada*_*dam 6

您无法追溯计算变量的确切占用空间,因为两个变量可以共享内存中相同的分配空间

让我们尝试在两个数组之间共享内存,我们发现分配第二个数组花费的内存是第一个数组的一半。当我们取消设置第一个时,几乎所有内存仍被第二个使用。

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)
Run Code Online (Sandbox Code Playgroud)

因此,我们不能得出第二个数组使用了一半内存的结论,因为当我们取消设置第一个数组时,它会变为 false。

要全面了解 PHP 中的内存分配方式及其用途,我建议您阅读以下文章:PHP 数组(和值)到底有多大?(提示:大!)

PHP 文档中的引用计数基础知识也有很多关于内存使用和共享数据段的引用计数的信息。

这里公开的不同解决方案对于近似值很有用,但没有一个可以处理 PHP 内存的微妙管理。

  1. 计算新分配的空间

如果您想要在分配之后使用新分配的空间,那么您必须memory_get_usage()在分配之前和之后使用它,因为将其与副本一起使用确实会给您带来对现实的错误看法。

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";
Run Code Online (Sandbox Code Playgroud)

请记住,如果您想存储第一个的结果memory_get_usage(),则该变量之前必须已经存在,并且memory_get_usage()必须之前再次调用,每个其他函数也是如此。

如果您想像上面的示例一样进行回显,则必须已经打开输出缓冲区,以避免打开输出缓冲区所需的内存。

  1. 计算所需空间

如果您想依赖函数来计算存储变量副本所需的空间,则以下代码负责不同的优化:

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044
Run Code Online (Sandbox Code Playgroud)

请注意,变量名称的大小对于分配的内存很重要。

  1. 检查你的代码!!

变量具有由 PHP 源代码中使用的内部 C 结构定义的基本大小。这个大小在数字的情况下不会波动。对于字符串,它将添加字符串的长度。

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;
Run Code Online (Sandbox Code Playgroud)

如果我们不考虑变量名的初始化,我们已经知道变量使用了多少(在数字和字符串的情况下):

数字时为 44 个字节

+ 如果是字符串则为 24 个字节

+ 字符串的长度(包括最后的NUL字符)

(这些数字可能会根据 PHP 版本而变化)

由于内存对齐,您必须四舍五入为 4 字节的倍数。如果变量位于全局空间中(不在函数内部),它还会再分配 64 个字节。

因此,如果您想使用本页中的代码之一,您必须检查使用一些简单测试用例(字符串或数字)的结果是否与这些数据匹配,同时考虑到本文中的每一项指示($_GLOBAL 数组,第一个函数调用,输出缓冲区,...)