Как определить объем памяти (размер) переменной?


103

Есть ли функция в PHP (или расширении PHP), чтобы узнать, сколько памяти использует данная переменная? sizeofпросто сообщает мне количество элементов / свойств.

memory_get_usageпомогает в том, что он дает мне размер памяти, используемый всем скриптом. Есть ли способ сделать это для одной переменной?

Обратите внимание, что это на машине разработки, поэтому загрузка расширений или инструментов отладки возможна.


Отредактировано - это 5 лет спустя, а некоторые проблемы до сих пор не
решены

Ответы:


48

Возможно, вам понадобится профилировщик памяти. Я собрал информацию из ТАК, но я скопировал кое-что важное, что также может вам помочь.

Как вы, наверное, знаете, Xdebug отказался от поддержки профилирования памяти с версии 2. *. Найдите строку «удаленные функции» здесь: http://www.xdebug.org/updates.php

Удаленные функции

Удалена поддержка профилирования памяти, так как это не работало должным образом.

Другие параметры профилировщика

профилировщик памяти 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

А потом в моем коде:

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

Наконец откройте callgrind.outфайл с помощью KCachegrind

Использование Google gperftools (рекомендуется!)

Прежде всего установите Google gperftools , загрузив последний пакет здесь: https://code.google.com/p/gperftools/

Потом как всегда:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

Теперь в вашем коде:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

Затем откройте свой терминал и запустите:

pprof --web /tmp/profile.heap

pprof создаст новое окно в вашем существующем сеансе браузера, как показано ниже:

Профилирование памяти PHP с помощью memprof и gperftools

Xhprof + Xhgui (лучший, на мой взгляд, для профилирования процессора и памяти)

С помощью Xhprof и Xhgui вы также можете профилировать использование процессора или только использование памяти, если это ваша проблема на данный момент. Это очень полные решения, они дают вам полный контроль, а журналы могут быть записаны как на mongo, так и в файловой системе.

Подробнее см. Здесь .

Черный огонь

Blackfire - это профилировщик PHP от SensioLabs, ребята из Symfony2 https://blackfire.io/

Если вы используете puphpet для настройки своей виртуальной машины, вы будете рады узнать, что она поддерживается ;-)

Xdebug и отслеживание использования памяти

XDEBUG2 - это расширение для PHP. Xdebug позволяет вам регистрировать все вызовы функций, включая параметры и возвращаемые значения в файл в различных форматах. Существует три формата вывода. Один предназначен для считывания человеком трассы, другой больше подходит для компьютерных программ, поскольку его легче анализировать, а последний использует HTML для форматирования трассировки. Вы можете переключаться между двумя разными форматами с помощью настройки. Пример доступен здесь

форп

forp простой, ненавязчивый, ориентированный на производство профилировщик PHP. Некоторые из функций:

  • измерение времени и выделенной памяти для каждой функции

  • использование процессора

  • файл и номер строки вызова функции

  • вывод в формате Google Trace Event

  • заголовок функций

  • группировка функций

  • псевдонимы функций (полезно для анонимных функций)

DBG

DBG - это полнофункциональный отладчик php, интерактивный инструмент, который помогает вам отлаживать скрипты php. Он работает на веб-сервере производства и / или разработки и позволяет отлаживать сценарии локально или удаленно, из среды IDE или консоли. Его функции:

  • Удаленная и локальная отладка

  • Явная и неявная активация

  • Стек вызовов, включая вызовы функций, вызовы динамических и статических методов с их параметрами

  • Навигация по стеку вызовов с возможностью оценки переменных в соответствующих (вложенных) местах

  • Шаг в / Выйти / Перейти / Бежать к курсору

  • Условные точки останова

  • Глобальные точки останова

  • Ведение журнала ошибок и предупреждений

  • Несколько одновременных сеансов для параллельной отладки

  • Поддержка интерфейсов GUI и CLI

  • Поддерживаются сети IPv6 и IPv4

  • Все данные, передаваемые отладчиком, можно дополнительно защитить с помощью SSL.


2
Это именно та информация, которую я искал, спасибо.
Писквор покинул здание

93

Нет прямого способа получить использование памяти одной переменной, но, как предложил Гордон, вы можете использовать memory_get_usage. Это вернет общий объем выделенной памяти, поэтому вы можете использовать обходной путь и измерить использование до и после, чтобы получить использование одной переменной. Это немного взломано, но должно работать.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

Обратите внимание, что это никоим образом не является надежным методом, вы не можете быть уверены, что ничто другое не касается памяти при назначении переменной, поэтому его следует использовать только как приближение.

Фактически вы можете превратить это в функцию, создав копию переменной внутри функции и измерив используемую память. Не тестировал, но в принципе не вижу в этом ничего плохого:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

14
$tmp = $varсоздаст неглубокую копию. Это не будет выделять больше памяти, пока не будет изменен $ tmp.
Гордон

@ Гордон, ты прав, я упустил этот момент. Поскольку я не могу найти правильный способ изменить переменную без изменения ее типа или размера, я оставлю это в покое. Возможно, кому-то удастся придумать подходящую идею :)
Тату Ульманен

7
как насчет $tmp = unserialize(serialize($var)); Это объединит подход Aistina, описанный выше.
Гордон

3
Кроме того, поскольку $varэто уже поверхностная копия или ссылка на то, что было передано в функцию, вам не нужно $tmp, но вы можете переназначить $var. Это сохраняет внутреннюю ссылку от $tmpдо $var.
Гордон

Нет ли более элегантный способ разыменования $tmpот $var?
Tomáš Zato - Reinstate Monica

24

Нет, нет. Но вы можете serialize($var)проверить strlenрезультат для приблизительности.


Это намного лучший подход, так как он позволяет избежать всего GC.
Gleno

12
Это ужасное приближение. Каждый элемент в массиве в PHP составляет ~ 80 байт, но при этом strlen(serialize(array(1,2,3)))равен 30.
gsnedders

2
@ Аистина, -1. вы измеряете не то. Переменная и сериализованная переменная - это две совершенно разные вещи, которые дают совершенно разные результаты.
Pacerier

1
Не только это, но и он полностью не работает с некоторыми несериализуемыми структурами данных, например, с циклическими ссылками.
duskwuff -inactive-

20

В ответ Тату Ульманенс отвечает:

Следует отметить, что $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;
}

Извините, что добавляю это как дополнительный ответ, но я пока не могу прокомментировать ответ.

Обновление: * 8 не определено. Очевидно, это может зависеть от версии php и, возможно, от 64/32 бит.


4
Вы можете объяснить почему * 8? Спасибо!
sierrasdetandil

@sierrasdetandil Похоже, что $ start_memory занимает не только PHP_INT_SIZEбайты, а PHP_INT_SIZE*8. Вы можете попробовать, вызвав эту функцию, она должна вернуть 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8не кажется постоянным. После вашей функции комментариев в моей системе разработки (PHP 5.6.19) он возвращается -16. Кроме того, что интересно, php -aвызов двух строк функции дает разные значения.
Paul DelRe

@PaulDelRe да, вероятно, это зависит от версии / 64bit такого рода вещей.
п.

теперь фатальная ошибка происходит при вызове unserialize (). Это не поможет! Если переменная настолько велика, что у нее заканчивается память, вызов функции для этой переменной будет использовать БОЛЬШЕ памяти. :(
john ktejik

4

Видеть:

Обратите внимание, что это не даст вам использования памяти конкретной переменной. Но вы можете вызывать эти функции до и после присвоения переменной, а затем сравнивать значения. Это должно дать вам представление об используемой памяти.

Вы также можете взглянуть на расширение PECL Memtrack , хотя документации немного не хватает, если не сказать, практически не существует.


Да. Его можно косвенно использовать для ответа на вопрос.
Notinlist

3

Вы можете выбрать вычисление разницы в памяти для возвращаемого значения обратного вызова. Это более элегантное решение, доступное в PHP 5.3+.

function calculateFootprint($callback) {
    $startMemory = memory_get_usage();
    $result = call_user_func($callback);
    return memory_get_usage() - $startMemory;
}

$memoryFootprint = calculateFootprint(
    function() {
        return range(1, 1000000);
    }
);

echo ($memoryFootprint / (1024 * 1024)) . ' MB' . PHP_EOL;

3

Вы не можете ретроспективно рассчитать точный размер переменной, поскольку две переменные могут совместно использовать одно и то же выделенное пространство в памяти.

Попробуем разделить память между двумя массивами, мы увидим, что выделение второго массива занимает половину памяти первого. Когда мы отключили первый, почти вся память по-прежнему используется вторым.

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)

Таким образом, мы не можем сделать вывод, что второй массив использует половину памяти, так как становится ложным, когда мы сбрасываем первый.

Чтобы получить полное представление о том, как выделяется память в 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";

Помните, что если вы хотите сохранить результат первого 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

Обратите внимание, что размер имени переменной имеет значение в выделенной памяти.

  1. Проверьте свой код !!

Переменная имеет базовый размер, определяемый внутренней структурой C, используемой в исходном коде PHP. В случае цифр этот размер не меняется. Для строк это добавит длину строки.

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;

Если мы не принимаем во внимание инициализацию имени переменной, мы уже знаем, сколько использует переменная (в случае чисел и строк):

44 байта в случае чисел

+ 24 байта в случае строк

+ длина строки (включая последний символ NUL)

(эти числа могут меняться в зависимости от версии PHP)

Вы должны округлить до кратного 4 байта из-за выравнивания памяти. Если переменная находится в глобальном пространстве (не внутри функции), она также выделит еще 64 байта.

Поэтому, если вы хотите использовать один из кодов на этой странице, вы должны проверить, что результат, используя несколько простых тестовых примеров (строки или числа), соответствует этим данным с учетом всех указаний в этом сообщении (массив $ _GLOBAL, первый вызов функции, выходной буфер, ...)


1
... и это даже прежде чем попасть в внутренности zvalue, is_refа затем скопировать на записи. Спасибо.
Писквор покинул здание

1
Благодаря вам я пропустил эту страницу в Руководстве по PHP. Я добавил ссылку, чтобы завершить свой ответ (но я думаю, вы уже это читали).
Адам

2

У меня была аналогичная проблема, и я использовал решение, чтобы записать переменную в файл, а затем запустить для него размер файла (). Примерно так (непроверенный код):

function getVariableSize ( $foo ) 
{
    $tmpfile = "temp-" . microtime(true) . ".txt";
    file_put_contents($tmpfile, $foo);
    $size = filesize($tmpfile);
    unlink($tmpfile);
    return $size;
}

Это решение не очень быстрое, потому что оно включает в себя дисковый ввод-вывод, но оно должно дать вам нечто гораздо более точное, чем уловки memory_get_usage. Это просто зависит от того, какая точность вам нужна.


Следует отметить, что это решение работает только для строк и одномерных массивов строк, и его использование strlenбудет проще.
Адам


1
function mesure($var){
    $start = memory_get_usage();
    if(is_string($var)){
        $newValue = $var . '';
    }elseif(is_numeric($var)){
        $newValue = $var + 0;
    }elseif(is_object($var)){
        $newValue = clone $var;
    }elseif(is_array($var)){
        $newValue = array_flip($var, []);
    }
    return memory_get_usage() - $start;
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.