Как определить объем памяти (размер) переменной? - PullRequest
99 голосов
/ 03 февраля 2010

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

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

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

Ответы [ 11 ]

92 голосов
/ 03 февраля 2010

Нет прямого способа получить использование памяти одной переменной, но, как предложил Гордон, вы можете использовать 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;
}
42 голосов
/ 20 декабря 2015

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

Как вы, наверное, знаете, 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 memory profiling with memprof and gperftools

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

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

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

Blackfire

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

23 голосов
/ 03 февраля 2010

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

20 голосов
/ 07 ноября 2012

В ответ на ответ Tatu Ulmanens:

Следует отметить, что $start_memory сама займет память (PHP_INT_SIZE * 8).

Таким образом, вся функция должна стать:1007 *

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 голосов
/ 03 февраля 2010

См .:

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

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

3 голосов
/ 15 декабря 2015

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

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

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 байта в случае чисел

* +1058 * & плюс; 24 байта в случае строк

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

(эти цифры могут меняться в зависимости от версии PHP)

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

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

3 голосов
/ 07 июня 2013

Вы можете выбрать вычисление разницы в памяти для возвращаемого значения обратного вызова.Это более элегантное решение, доступное в 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;
2 голосов
/ 30 декабря 2013

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

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

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

1 голос
/ 13 мая 2014
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;
}
1 голос
/ 03 февраля 2010

Никогда не пробовал, но трассировки Xdebug с xdebug.collect_assignment s может быть достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...