Wie bestimme ich den Speicherbedarf (die Größe) einer Variablen?


102

Gibt es eine Funktion in PHP (oder einer PHP-Erweiterung), um herauszufinden, wie viel Speicher eine bestimmte Variable verwendet? sizeofsagt mir nur die Anzahl der Elemente / Eigenschaften.

memory_get_usagehilft dabei, dass es mir die Speichergröße gibt, die vom gesamten Skript verwendet wird. Gibt es eine Möglichkeit, dies für eine einzelne Variable zu tun?

Beachten Sie, dass sich dies auf einem Entwicklungscomputer befindet, sodass das Laden von Erweiterungen oder Debug-Tools möglich ist.


Bearbeitet - es ist 5 Jahre später und einige Probleme sind noch etwas ungelöst :(
Piskvor verließ das Gebäude

Antworten:


46

Sie benötigen wahrscheinlich einen Speicherprofiler. Ich habe Informationen für SO gesammelt, aber ich habe einige wichtige Dinge kopiert, die Ihnen auch helfen können.

Wie Sie wahrscheinlich wissen, hat Xdebug die Unterstützung für Speicherprofile seit der 2. * -Version eingestellt. Bitte suchen Sie hier nach der Zeichenfolge "entfernte Funktionen": http://www.xdebug.org/updates.php

Funktionen entfernt

Die Unterstützung für die Speicherprofilerstellung wurde entfernt, da dies nicht ordnungsgemäß funktionierte.

Andere Profiler-Optionen

PHP-Speicher-Profiler

https://github.com/arnaud-lb/php-memory-profiler . Folgendes habe ich auf meinem Ubuntu-Server getan, um es zu aktivieren:

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

Und dann in meinem Code:

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

Öffnen Sie schließlich die callgrind.outDatei mit KCachegrind

Verwendung von Google Gperftools (empfohlen!)

Installieren Sie zunächst die Google Gperftools, indem Sie das neueste Paket hier herunterladen: https://code.google.com/p/gperftools/

Dann wie immer:

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

Jetzt in Ihrem Code:

memprof_enable();

// do your magic

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

Öffnen Sie dann Ihr Terminal und starten Sie:

pprof --web /tmp/profile.heap

pprof erstellt in Ihrer vorhandenen Browsersitzung ein neues Fenster mit den folgenden Angaben :

PHP-Speicherprofilerstellung mit memprof und gperftools

Xhprof + Xhgui (meiner Meinung nach das Beste, um sowohl CPU als auch Speicher zu profilieren)

Mit Xhprof und Xhgui können Sie auch die CPU-Auslastung oder nur die Speichernutzung profilieren, wenn dies momentan Ihr Problem ist. Es ist eine sehr vollständige Lösung, es gibt Ihnen die volle Kontrolle und die Protokolle können sowohl auf Mongo als auch im Dateisystem geschrieben werden.

Weitere Details finden Sie hier .

Schwarzes Feuer

Blackfire ist ein PHP-Profiler von SensioLabs, den Symfony2-Jungs https://blackfire.io/

Wenn Sie Ihre virtuelle Maschine mit puphpet einrichten, werden Sie froh sein zu wissen, dass sie unterstützt wird ;-)

Xdebug- und Tracing-Speichernutzung

XDEBUG2 ist eine Erweiterung für PHP. Mit Xdebug können Sie alle Funktionsaufrufe einschließlich Parameter und Rückgabewerte in einer Datei in verschiedenen Formaten protokollieren. Es gibt drei Ausgabeformate. Eine ist als von Menschen lesbare Ablaufverfolgung gedacht, eine andere eignet sich besser für Computerprogramme, da sie einfacher zu analysieren ist, und die letzte verwendet HTML zum Formatieren der Ablaufverfolgung. Mit der Einstellung können Sie zwischen den beiden verschiedenen Formaten wechseln. Ein Beispiel wäre hier verfügbar

für P

forp einfacher, nicht aufdringlicher, produktionsorientierter PHP-Profiler. Einige der Funktionen sind:

  • Messung der Zeit und des zugewiesenen Speichers für jede Funktion

  • CPU auslastung

  • Datei- und Zeilennummer des Funktionsaufrufs

  • Ausgabe als Trace-Ereignisformat von Google

  • Beschriftung von Funktionen

  • Gruppierung von Funktionen

  • Aliase von Funktionen (nützlich für anonyme Funktionen)

DBG

DBG ist ein voll funktionsfähiger PHP-Debugger, ein interaktives Tool, mit dem Sie PHP-Skripte debuggen können. Es funktioniert auf einem Produktions- und / oder Entwicklungs-WEB-Server und ermöglicht es Ihnen, Ihre Skripte lokal oder remote über eine IDE oder Konsole zu debuggen. Folgende Funktionen stehen zur Verfügung:

  • Remote- und lokales Debugging

  • Explizite und implizite Aktivierung

  • Aufrufstapel, einschließlich Funktionsaufrufe, dynamische und statische Methodenaufrufe, mit ihren Parametern

  • Navigation durch den Aufrufstapel mit der Möglichkeit, Variablen an entsprechenden (verschachtelten) Stellen auszuwerten

  • Step in / Step out / Step over / Run to Cursor-Funktionalität

  • Bedingte Haltepunkte

  • Globale Haltepunkte

  • Protokollierung auf Fehler und Warnungen

  • Mehrere gleichzeitige Sitzungen zum parallelen Debuggen

  • Unterstützung für GUI- und CLI-Frontends

  • IPv6- und IPv4-Netzwerke werden unterstützt

  • Alle vom Debugger übertragenen Daten können optional mit SSL geschützt werden


2
Das sind genau die Informationen, nach denen ich gesucht habe, danke.
Piskvor verließ das Gebäude

93

Es gibt keine direkte Möglichkeit, die Speichernutzung einer einzelnen Variablen zu ermitteln, aber wie Gordon vorgeschlagen hat, können Sie diese verwenden memory_get_usage. Dadurch wird die Gesamtmenge des zugewiesenen Speichers zurückgegeben, sodass Sie eine Problemumgehung verwenden und die Nutzung vorher und nachher messen können, um die Nutzung einer einzelnen Variablen abzurufen. Das ist ein bisschen hacky, aber es sollte funktionieren.

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

Beachten Sie, dass dies in keiner Weise eine zuverlässige Methode ist. Sie können nicht sicher sein, dass beim Zuweisen der Variablen nichts anderes den Speicher berührt. Daher sollte dies nur als Annäherung verwendet werden.

Sie können dies tatsächlich in eine Funktion umwandeln, indem Sie eine Kopie der Variablen innerhalb der Funktion erstellen und den verwendeten Speicher messen. Ich habe das noch nicht getestet, aber im Prinzip sehe ich nichts falsch daran:

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

14
$tmp = $varerstellt eine flache Kopie. Dadurch wird nicht mehr Speicher zugewiesen, bis $ tmp geändert wird.
Gordon

@ Gordon, du hast recht, ich habe diesen Punkt irgendwie übersehen. Da ich keinen geeigneten Weg finden kann, um die Variable zu ändern, ohne ihren Typ oder ihre Größe zu ändern, lasse ich das so. Vielleicht kann sich jemand eine richtige Idee
einfallen lassen

7
wie wäre es $tmp = unserialize(serialize($var)); Dies würde Aistinas obigen Ansatz kombinieren.
Gordon

3
Da $vares sich bereits um eine flache Kopie oder Referenz handelt, die an die Funktion übergeben wurde, benötigen Sie diese nicht $tmp, können sie jedoch neu zuweisen $var. Dies speichert die interne Referenz von $tmpbis $var.
Gordon

Gibt es nicht etwas eleganteren Weg dereferenzieren $tmpaus $var?
Tomáš Zato - Wiedereinsetzung Monica

24

Nein, da ist kein. Sie können das Ergebnis jedoch auf eine Annäherung serialize($var)überprüfen strlen.


Dies ist ein viel besserer Ansatz, da er die gesamte GC-Sache vermeidet.
Gleno

12
Es ist eine schreckliche Annäherung. Jedes Element in einem Array in PHP ist ~ 80 Bytes, aber strlen(serialize(array(1,2,3)))ist 30.
gsnedders

2
@Aistina, -1. Sie messen das Falsche. Die Variable und die serialisierte Variable sind zwei völlig verschiedene Dinge und führen zu völlig unterschiedlichen Ergebnissen.
Pacerier

1
Darüber hinaus schlägt es bei bestimmten nicht serialisierbaren Datenstrukturen, z. B. Zirkelverweisen, vollständig fehl .
Abenddämmerung -inaktiv-

20

Als Antwort auf Tatu Ulmanens Antwort:

Es ist zu beachten, dass $start_memoryselbst Speicher belegt wird ( PHP_INT_SIZE * 8).

So sollte die ganze Funktion werden:

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

Es tut mir leid, dies als zusätzliche Antwort hinzuzufügen, aber ich kann eine Antwort noch nicht kommentieren.

Update: Die * 8 ist nicht definitiv. Es kann anscheinend von der PHP-Version und möglicherweise von 64/32 Bit abhängen.


4
Kannst du erklären warum * 8? Vielen Dank!
Sierra Detandil

@sierrasdetandil Es scheint, dass $ start_memory nicht nur PHP_INT_SIZEBytes, sondern auch belegt PHP_INT_SIZE*8. Sie können versuchen , dass durch diese Funktion aufrufen, sollte es zurückgeben 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8scheint nicht konstant zu sein. Nach Ihrer Kommentarfunktion auf meinem Entwicklungssystem (PHP 5.6.19) wird es zurückgegeben -16. Interessanterweise ergeben sich aus dem php -aAufruf der beiden Zeilen der Funktion verschiedene unterschiedliche Werte.
Paul DelRe

@PaulDelRe ja, wahrscheinlich hängt es von der Version / 64bit dieser Art von Sachen ab.
Abs.

Jetzt tritt der schwerwiegende Fehler beim Aufruf von unserialize () auf. Das ist keine Hilfe! Wenn eine Variable so groß ist, dass ihr der Speicher ausgeht, verbraucht das Aufrufen einer Funktion für diese Variable noch MEHR Speicher. :(
John Ktejik

4

Sehen:

Beachten Sie, dass Sie dadurch nicht die Speichernutzung einer bestimmten Variablen erhalten. Sie können diese Funktion jedoch vor und nach dem Zuweisen der Variablen aufrufen und dann die Werte vergleichen. Das sollte Ihnen eine Vorstellung von dem verwendeten Speicher geben.

Sie können sich auch die PECL-Erweiterung Memtrack ansehen , obwohl die Dokumentation etwas fehlt, wenn nicht sogar praktisch nicht vorhanden ist.


Ja. Es kann indirekt zur Beantwortung der Frage verwendet werden.
Notinlist

3

Sie können die Speicherdifferenz für einen Rückrufrückgabewert berechnen. Es ist eine elegantere Lösung, die in PHP 5.3+ verfügbar ist.

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

Sie können den genauen Footprint einer Variablen nicht nachträglich berechnen, da zwei Variablen denselben zugewiesenen Speicherplatz im Speicher gemeinsam nutzen können

Versuchen wir, den Speicher zwischen zwei Arrays zu teilen. Wir sehen, dass das Zuweisen des zweiten Arrays die Hälfte des Speichers des ersten Arrays kostet. Wenn wir den ersten deaktivieren, wird fast der gesamte Speicher noch vom zweiten verwendet.

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)

Wir können also nicht schließen, dass das zweite Array die Hälfte des Speichers verwendet, da es falsch wird, wenn wir das erste deaktivieren.

Um einen vollständigen Überblick darüber zu erhalten, wie der Speicher in PHP zugewiesen wird und für welche Verwendung, empfehle ich Ihnen, den folgenden Artikel zu lesen: Wie groß sind PHP-Arrays (und -Werte) wirklich? (Hinweis: GROSS!)

Die Grundlagen der Referenzzählung in der PHP-Dokumentation enthalten auch viele Informationen zur Speichernutzung, und Referenzen zählen für gemeinsam genutzte Datensegmente.

Die verschiedenen hier vorgestellten Lösungen sind gut für Annäherungen, aber keine kann die subtile Verwaltung des PHP-Speichers bewältigen.

  1. Berechnung des neu zugewiesenen Speicherplatzes

Wenn Sie den neu zugewiesenen Speicherplatz nach einer Zuweisung verwenden möchten, müssen Sie ihn memory_get_usage()vor und nach der Zuweisung verwenden, da die Verwendung mit einer Kopie eine fehlerhafte Ansicht der Realität ergibt.

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

Denken Sie daran, dass, wenn Sie das Ergebnis des ersten speichern möchten memory_get_usage(), die Variable bereits vorher vorhanden sein muss und ein memory_get_usage()anderes vorheriges Mal aufgerufen werden muss, sowie jede andere Funktion auch.

Wenn Sie wie im obigen Beispiel Echo wiedergeben möchten, muss Ihr Ausgabepuffer bereits geöffnet sein, um zu vermeiden, dass der zum Öffnen des Ausgabepuffers erforderliche Abrechnungsspeicher benötigt wird.

  1. Berechnung des benötigten Platzes

Wenn Sie sich auf eine Funktion verlassen möchten, um den erforderlichen Speicherplatz zum Speichern einer Kopie einer Variablen zu berechnen, werden im folgenden Code verschiedene Optimierungen vorgenommen:

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

Beachten Sie, dass die Größe des Variablennamens im zugewiesenen Speicher von Bedeutung ist.

  1. Überprüfen Sie Ihren Code!

Eine Variable hat eine Grundgröße, die durch die im PHP-Quellcode verwendete innere C-Struktur definiert wird. Diese Größe schwankt bei Zahlen nicht. Bei Zeichenfolgen wird die Länge der Zeichenfolge hinzugefügt.

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;

Wenn wir die Initialisierung des Variablennamens nicht berücksichtigen, wissen wir bereits, wie viel eine Variable verwendet (bei Zahlen und Zeichenfolgen):

44 Bytes bei Zahlen

+ 24 Bytes bei Strings

+ die Länge der Zeichenfolge (einschließlich des letzten NUL-Zeichens)

(Diese Zahlen können sich je nach PHP-Version ändern.)

Aufgrund der Speicherausrichtung müssen Sie auf ein Vielfaches von 4 Bytes aufrunden. Befindet sich die Variable im globalen Bereich (nicht innerhalb einer Funktion), werden 64 weitere Bytes zugewiesen.

Wenn Sie also einen der Codes auf dieser Seite verwenden möchten, müssen Sie anhand einiger einfacher Testfälle (Zeichenfolgen oder Zahlen) überprüfen, ob das Ergebnis mit diesen Daten übereinstimmt, wobei alle Angaben in diesem Beitrag berücksichtigt werden ($ _GLOBAL-Array, erster Funktionsaufruf, Ausgabepuffer, ...)


1
... und das ist noch bevor wir in die Interna bekommen zvalue, is_refund Copy-on-Write. Danke dir.
Piskvor verließ das Gebäude

1
Dank dir habe ich diese Seite im PHP-Handbuch verpasst. Ich habe den Link hinzugefügt, um meine Antwort zu vervollständigen (aber ich denke, Sie haben das bereits gelesen).
Adam

2

Ich hatte ein ähnliches Problem, und die Lösung, die ich verwendete, bestand darin, die Variable in eine Datei zu schreiben und dann filesize () darauf auszuführen. Ungefähr so ​​(ungetesteter Code):

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

Diese Lösung ist nicht besonders schnell, da es sich um Festplatten-E / A handelt, aber sie sollte Ihnen etwas viel Genaueres bieten als die Tricks memory_get_usage. Es kommt nur darauf an, wie viel Präzision Sie benötigen.


Es sollte beachtet werden, dass diese Lösung nur für Zeichenfolgen und ein eindimensionales Array von Zeichenfolgen funktioniert und dass die Verwendung strleneinfacher wäre.
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;
}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.