Como determinar a pegada de memória (tamanho) de uma variável?

102

Existe uma função no PHP (ou uma extensão do PHP) para descobrir quanta memória usa uma determinada variável? sizeofapenas me diz o número de elementos / propriedades.

memory_get_usageajuda na medida em que me dá o tamanho da memória usado por todo o script. Existe uma maneira de fazer isso para uma única variável?

Observe que isso está em uma máquina de desenvolvimento, portanto, carregar extensões ou ferramentas de depuração é viável.

Piskvor saiu do prédio
fonte
Editado - 5 anos depois, e alguns problemas ainda não foram resolvidos :(
Piskvor saiu do prédio em

Respostas:

46

Você provavelmente precisa de um Memory Profiler. Eu coletei informações para ASSIM, mas copiei algumas coisas importantes que podem ajudá-lo também.

Como você provavelmente sabe, o Xdebug abandonou o suporte a perfis de memória desde a versão 2. *. Pesquise a string "funções removidas" aqui: http://www.xdebug.org/updates.php

Funções removidas

Removido o suporte para perfis de memória, pois não funcionava corretamente.

Outras opções do Profiler

php-memory-profiler

https://github.com/arnaud-lb/php-memory-profiler . Isso é o que fiz no meu servidor Ubuntu para ativá-lo:

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

E então no meu código:

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

Finalmente abra o callgrind.outarquivo com o KCachegrind

Usando Google gperftools (recomendado!)

Em primeiro lugar, instale o Google gperftools baixando o pacote mais recente aqui: https://code.google.com/p/gperftools/

Então, como sempre:

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

Agora em seu código:

memprof_enable();

// do your magic

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

Em seguida, abra seu terminal e execute:

pprof --web /tmp/profile.heap

pprof criará uma nova janela em sua sessão de navegador existente com algo como mostrado abaixo:

Perfil de memória PHP com memprof e gperftools

Xhprof + Xhgui (o melhor na minha opinião para o perfil de CPU e memória)

Com o Xhprof e o Xhgui, você também pode traçar o perfil do uso da CPU ou apenas do uso da memória, se esse for o seu problema no momento. É uma solução muito completa, dá-lhe total controlo e os logs podem ser escritos tanto no mongo como no sistema de ficheiros.

Para mais detalhes veja aqui .

Fogo Preto

Blackfire é um profiler PHP da SensioLabs, os caras do Symfony2 https://blackfire.io/

Se você usar o fantoche para configurar sua máquina virtual, ficará feliz em saber que é compatível ;-)

Xdebug e rastreamento de uso de memória

XDEBUG2 é uma extensão para PHP. O Xdebug permite que você registre todas as chamadas de função, incluindo parâmetros e valores de retorno para um arquivo em formatos diferentes. Existem três formatos de saída. Um deve ser um rastreamento legível por humanos, outro é mais adequado para programas de computador, pois é mais fácil de analisar, e o último usa HTML para formatar o rastreamento. Você pode alternar entre os dois formatos diferentes com a configuração. Um exemplo estaria disponível aqui

para p

forp simples, não intrusivo, orientado à produção, criador de perfil de PHP. Alguns dos recursos são:

  • medição de tempo e memória alocada para cada função

  • utilização do CPU

  • arquivo e número da linha da chamada de função

  • saída como formato de evento de rastreamento do Google

  • legenda de funções

  • agrupamento de funções

  • aliases de funções (útil para funções anônimas)

DBG

DBG é um depurador de php completo, uma ferramenta interativa que ajuda a depurar scripts de php. Funciona em um servidor WEB de produção e / ou desenvolvimento e permite depurar seus scripts local ou remotamente, a partir de um IDE ou console e seus recursos são:

  • Depuração remota e local

  • Ativação explícita e implícita

  • Pilha de chamadas, incluindo chamadas de função, chamadas de método estático e dinâmico, com seus parâmetros

  • Navegação pela pilha de chamadas com capacidade de avaliar variáveis ​​em locais correspondentes (aninhados)

  • Funcionalidade Step in / Step out / Step over / Run to cursor

  • Breakpoints condicionais

  • Pontos de interrupção globais

  • Registro de erros e avisos

  • Várias sessões simultâneas para depuração paralela

  • Suporte para interfaces GUI e CLI

  • Redes IPv6 e IPv4 suportadas

  • Todos os dados transferidos pelo depurador podem ser protegidos opcionalmente com SSL

Vineet 1982
fonte
2
Essa é exatamente a informação que eu estava procurando, obrigado.
Piskvor saiu do prédio em
93

Não há uma maneira direta de obter o uso de memória de uma única variável, mas, como sugeriu Gordon, você pode usar memory_get_usage. Isso retornará a quantidade total de memória alocada, para que você possa usar uma solução alternativa e medir o uso antes e depois de obter o uso de uma única variável. Isso é um pouco hackeado, mas deve funcionar.

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

Observe que este não é, de forma alguma, um método confiável, você não pode ter certeza de que nada mais tocará na memória ao atribuir a variável, então isso deve ser usado apenas como uma aproximação.

Você pode realmente transformar isso em uma função criando uma cópia da variável dentro da função e medindo a memória usada. Não testei isso, mas, em princípio, não vejo nada de errado nisso:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}
Tatu Ulmanen
fonte
14
$tmp = $varcriará uma cópia superficial. Isso não alocará mais memória até que $ tmp seja modificado.
Gordon
@Gordon, você está certo, meio que esqueci esse ponto. Como não consigo descobrir uma maneira adequada de modificar a variável sem alterar seu tipo ou tamanho, deixarei como está. Talvez alguém possa ter uma ideia adequada :)
Tatu Ulmanen
7
que tal $tmp = unserialize(serialize($var)); Isso combinaria a abordagem de Aistina acima.
Gordon
3
além disso, como $varjá é uma cópia superficial ou referência do que foi passado para a função, você não precisa $tmp, mas pode reatribuir a $var. Isso salva a referência interna de $tmpa $var.
Gordon
Não há alguma maneira mais elegante excluir a referência $tmpde $var?
Tomáš Zato - Reintegração de Monica em
24

Não, não há. Mas você pode serialize($var)e verificar o strlendo resultado para uma aproximação.

Aistina
fonte
Esta é uma abordagem muito melhor, pois evita toda a coisa de GC.
Gleno
12
É uma aproximação terrível. Cada item em uma matriz em PHP tem ~ 80 bytes, mas strlen(serialize(array(1,2,3)))é 30.
gsnedders
2
@Aistina, -1. você está medindo a coisa errada. A variável e a variável serializada são duas coisas totalmente diferentes e darão resultados completamente diferentes.
Pacerier
1
Não apenas isso, mas irá falhar completamente em certas estruturas de dados não serializáveis, por exemplo, referências circulares.
duskwuff -inactive-
20

Em resposta à resposta de Tatu Ulmanens:

Deve-se notar que $start_memoryele próprio ocupará memória ( PHP_INT_SIZE * 8).

Portanto, toda a função deve ser:

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

Desculpe adicionar isso como uma resposta extra, mas ainda não posso comentar uma resposta.

Atualização: O * 8 não é definitivo. Pode depender aparentemente da versão php e possivelmente de 64/32 bits.

pára
fonte
4
Você pode explicar por quê * 8? Obrigado!
sierrasdetandil
@sierrasdetandil Parece que $ start_memory não ocupa apenas PHP_INT_SIZEbytes, mas PHP_INT_SIZE*8. Você pode tentar isso chamando esta função, ela deve retornar 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para
8não parece constante. Seguindo sua função de comentário em meu sistema dev (PHP 5.6.19), ele retorna -16. Além disso, curiosamente, a partir de php -a, chamar as duas linhas da função fornece vários valores diferentes.
Paul DelRe
@PaulDelRe sim, provavelmente depende da versão / 64 bits desse tipo de coisa.
para
agora, o erro fatal acontece na chamada unserialize (). Isso não ajuda! Se uma variável é tão grande que fica sem memória, chamar uma função nessa variável usará ainda MAIS memória. :(
john ktejik
4

Vejo:

Observe que isso não fornecerá o uso de memória de uma variável específica. Mas você pode fazer chamadas para essas funções antes e depois de atribuir a variável e, em seguida, comparar os valores. Isso deve dar uma ideia da memória usada.

Você também pode dar uma olhada na extensão PECL Memtrack , embora a documentação seja um pouco deficiente, se não para dizer, virtualmente inexistente.

Gordon
fonte
Sim. Pode ser usado indiretamente para responder à pergunta.
Notinlist de
3

Você pode optar por calcular a diferença de memória em um valor de retorno de chamada. É uma solução mais elegante disponível no 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;
Liviu Bundă
fonte
3

Você não pode calcular retrospectivamente a pegada exata de uma variável, pois duas variáveis ​​podem compartilhar o mesmo espaço alocado na memória

Vamos tentar compartilhar memória entre dois arrays, vemos que alocar o segundo array custa metade da memória do primeiro. Quando removemos o primeiro, quase toda a memória ainda é usada pelo segundo.

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)

Portanto, não podemos concluir que o segundo array usa metade da memória, pois torna-se falso quando removemos o primeiro.

Para uma visão completa sobre como a memória é alocada no PHP e para qual uso, sugiro que você leia o seguinte artigo: Qual o tamanho real dos arrays (e valores) do PHP? (Dica: GRANDE!)

O Reference Counting Basics na documentação do PHP também contém muitas informações sobre o uso de memória e contagem de referências para segmentos de dados compartilhados.

As diferentes soluções expostas aqui são boas para aproximações, mas nenhuma pode lidar com o gerenciamento sutil de memória PHP.

  1. calcular o espaço recém-alocado

Se você quiser o espaço recém-alocado após uma atribuição, terá que usá-lo memory_get_usage()antes e depois da alocação, pois usá-lo com uma cópia lhe dá uma visão errônea da realidade.

// 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";

Lembre-se de que se você quiser armazenar o resultado da primeira memory_get_usage(), a variável já deve existir antes e memory_get_usage()deve ser chamada outra vez, e também todas as outras funções.

Se você quiser ecoar como no exemplo acima, seu buffer de saída já deve estar aberto para evitar a memória de contabilidade necessária para abrir o buffer de saída.

  1. calculando o espaço necessário

Se você quiser contar com uma função para calcular o espaço necessário para armazenar uma cópia de uma variável, o código a seguir cuida de diferentes otimizações:

<?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

Observe que o tamanho do nome da variável é importante na memória alocada.

  1. Verifique seu código !!

Uma variável tem um tamanho básico definido pela estrutura interna C usada no código-fonte do PHP. Este tamanho não varia no caso de números. Para strings, adicionaria o comprimento da string.

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;

Se não levarmos em consideração a inicialização do nome da variável, já sabemos quanto uma variável usa (no caso de números e strings):

44 bytes no caso de números

+ 24 bytes no caso de strings

+ o comprimento da string (incluindo o caractere NUL final)

(esses números podem mudar dependendo da versão do PHP)

Você deve arredondar para um múltiplo de 4 bytes devido ao alinhamento da memória. Se a variável estiver no espaço global (não dentro de uma função), ela também alocará mais 64 bytes.

Portanto, se você quiser usar um dos códigos dentro desta página, você deve verificar se o resultado usando alguns casos de teste simples (strings ou números) correspondem a esses dados levando em consideração cada uma das indicações neste artigo ($ _GLOBAL array, primeira chamada de função, buffer de saída, ...)

Adão
fonte
1
... e isso é mesmo antes de entrarmos as partes internas do zvalue, is_refe copiar-on-write. Obrigado.
Piskvor saiu do prédio em
1
Graças a você, perdi aquela página do Manual do PHP. Adicionei o link para completar minha resposta (mas acho que você já leu).
Adam
2

Tive um problema semelhante, e a solução que usei foi gravar a variável em um arquivo e executar filesize () nele. Mais ou menos assim (código não testado):

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

Esta solução não é terrivelmente rápida porque envolve E / S de disco, mas deve fornecer algo muito mais exato do que os truques de memory_get_usage. Depende apenas de quanta precisão você precisa.

Alan Bellows
fonte
Deve-se notar que esta solução só funciona para strings e array de strings de dimensão única e que o uso strlenseria mais fácil.
Adam
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;
}
Abdelilah
fonte
1

O script a seguir mostra o uso total de memória de uma única variável.

function getVariableUsage($var) {
  $total_memory = memory_get_usage();
  $tmp = unserialize(serialize($var));
  return memory_get_usage() - $total_memory; 
}

$var = "Hey, what's you doing?";
echo getVariableUsage($var);

Veja isso

http://www.phpzag.com/how-much-memory-do-php-variables-use/

vongani masanganye
fonte