変数のメモリフットプリント(サイズ)を決定する方法は?


102

PHP(またはPHP拡張)には、特定の変数が使用するメモリの量を調べる関数はありますか?sizeof要素/プロパティの数を教えてくれます。

memory_get_usageスクリプト全体で使用されるメモリサイズを取得するのに役立ちます。単一の変数に対してこれを行う方法はありますか?

これは開発マシン上にあるため、拡張機能やデバッグツールの読み込みが可能であることに注意してください。


編集-それは5年後であり、いくつかの問題はまだいくらか未解決です:(
Piskvorは建物を去り、

回答:


46

おそらくメモリプロファイラが必要です。私はSOから情報を集めましたが、あなたにも役立つかもしれないいくつかの重要なことをコピーしました。

おそらくご存じのとおり、Xdebugは2. *バージョン以降、メモリプロファイリングのサポートを終了しました。ここで「削除された関数」の文字列を検索してください:http : //www.xdebug.org/updates.php

削除された機能

正しく動作しなかったため、メモリプロファイリングのサポートを削除しました。

その他のプロファイラーオプション

php-memory-profiler

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

最後にKCachegrindでcallgrind.outファイルを開きます

Google gperftoolsの使用(推奨!)

まず最初に、https//code.google.com/p/gperftools/から最新のパッケージをダウンロードして、Google 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は、既存のブラウザーセッションで、以下に示すような新しいウィンドウを作成します。

memprofおよびgperftoolsによるPHPメモリプロファイリング

Xhprof + Xhgui(私の意見では、CPUとメモリの両方をプロファイリングするのに最適)

XhprofXhguiそれは現時点ではあなたの問題だ場合は、同様または単にメモリ使用量、CPU使用率をプロファイリングすることができます。これは非常に完全なソリューションであり、完全に制御でき、ログはmongoまたはファイルシステムの両方に書き込むことができます。

詳細はこちらをご覧ください

ブラックファイア

Blackfireは、Symfony2の仲間であるSensioLabsによるPHPプロファイラーです。https: //blackfire.io/

puphpetを使用して仮想マシンをセットアップする場合は、サポートされていることを確認できます;-)

Xdebugとメモリ使用量のトレース

XDEBUG2はPHPの拡張機能です。Xdebugを使用すると、パラメーターや戻り値を含むすべての関数呼び出しをさまざまな形式でファイルに記録できます。3つの出力形式があります。1つは人間が読み取れるトレースを意味し、もう1つは解析が容易なためコンピュータープログラムにより適しています。最後の1つはHTMLを使用してトレースをフォーマットします。設定で2つの異なるフォーマットを切り替えることができます。例はここあります

forp

forpシンプル、非侵入型、プロダクション指向のPHPプロファイラー。一部の機能は次のとおりです。

  • 各機能の時間と割り当てられたメモリの測定

  • CPU使用率

  • 関数呼び出しのファイルと行番号

  • Googleのトレースイベント形式として出力

  • 関数のキャプション

  • 関数のグループ化

  • 関数のエイリアス(無名関数に役立ちます)

DBG

DBGは、フル機能のPHPデバッガーです。これは、PHPスクリプトのデバッグに役立つインタラクティブなツールです。本番および/または開発用のWEBサーバーで動作し、IDEまたはコンソールからローカルまたはリモートでスクリプトをデバッグできます。その機能は次のとおりです。

  • リモートおよびローカルのデバッグ

  • 明示的および暗黙的なアクティブ化

  • 関数呼び出し、動的および静的メソッド呼び出しを含む呼び出しスタックとそのパラメーター

  • 対応する(ネストされた)場所で変数を評価する機能を備えたコールスタックを介したナビゲーション

  • ステップイン/ステップアウト/ステップオーバー/カーソル機能まで実行

  • 条件付きブレークポイント

  • グローバルブレークポイント

  • エラーと警告のロギング

  • 並列デバッグのための複数の同時セッション

  • GUIおよびCLIフロントエンドのサポート

  • サポートされているIPv6およびIPv4ネットワーク

  • デバッガーによって転送されたすべてのデータは、オプションでSSLで保護できます


2
それがまさに私が探していた情報です、ありがとう。
Piskvorが2015

93

単一の変数のメモリ使用量を直接取得する方法はありませんが、Gordonが示唆したように、を使用できますmemory_get_usage。これにより、割り当てられたメモリの合計量が返されるため、回避策を使用して、1つの変数の使用量を取得する前と後に使用量を測定できます。これは少しハッキーですが、動作するはずです。

$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が変更されるまで、これはより多くのメモリを割り当てません。
ゴードン

@ゴードン、あなたは正しい、私はちょっとその点を見過ごしていた。変数のタイプやサイズを変更せずに変数を変更する適切な方法がわからないので、そのままにしておきます。おそらく誰かが適切なアイデアを思い付くでしょう:)
Tatu Ulmanen

7
いかが$tmp = unserialize(serialize($var))ですか; これは、上記のAistinaのアプローチを組み合わせたものです。
Gordon、

3
また、$varは関数に渡されたものの浅いコピーまたは参照であるため、は必要ありませんが$tmp、に再割り当てでき$varます。これにより、内部参照がから$tmpに保存され$varます。
ゴードン

間接参照するためにいくつかのよりエレガントな方法はありません$tmpからは$var
トマーシュZato -復活モニカ

24

いいえ、ありません。ただし、結果のを概算してserialize($var)確認できますstrlen


これは、GC全体を回避するため、はるかに優れたアプローチです。
グレノ2013

12
それは恐ろしい近似です。PHPの配列内のすべての項目は〜80バイトであり、まだstrlen(serialize(array(1,2,3)))30である
gsnedders

2
@ Aistina、-1。あなたは間違ったものを測定しています。変数とシリアル化された変数は2つのまったく異なるものであり、まったく異なる結果をもたらします。
ペーチェリエ2013

1
それだけでなく、循環参照など、シリアル化できない特定のデータ構造では完全に失敗ます。
duskwuff -inactive-

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;
}

これを追加の回答として追加して申し訳ありませんが、回答にはまだコメントできません。

更新:* 8は明確ではありません。どうやらphpのバージョンとおそらく64/32ビットに依存している可能性があります。


4
理由を説明できますか* 8?ありがとう!
sierrasdetandil 2013

それは$ start_memoryは取らないようだ@sierrasdetandilだけ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; }
パラ

8一定していないようです。私の開発システム(PHP 5.6.19)でのコメント関数に従って、を返します-16。また、興味深いことに、からphp -a、関数の2行を呼び出すと、さまざまな異なる値が得られます。
Paul DelRe 2016年

@PaulDelReはい、おそらくそれはバージョン/ 64ビットのこの種のものに依存しています。
パラ

致命的なエラーがunserialize()呼び出しで発生するようになりました。仕方ない!変数が大きすぎてメモリが不足している場合、その変数で関数を呼び出すと、さらに多くのメモリが消費されます。:(
john ktejik

4

見る:

ただし、これは特定の変数のメモリ使用量を提供しないことに注意してください。ただし、変数を割り当てる前後にこれらの関数を呼び出して、値を比較することができます。これにより、使用されているメモリがわかります。

PECL拡張機能Memtrackを確認することもできますが、ドキュメントには事実上、存在しないというか、少し不足しています。


はい。質問に回答するために間接的に使用できます。
ノーリスト

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

2つの変数がメモリ内の同じ割り当てられたスペースを共有できるため、変数の正確なフットプリントを遡及的に計算することはできません

2つの配列間でメモリを共有してみましょう。2番目の配列を割り当てると、最初の配列の半分のメモリが消費されます。最初のメモリを設定解除しても、ほぼすべてのメモリが2番目のメモリで使用されています。

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)

したがって、最初の配列を設定解除するとfalseになるため、2番目の配列がメモリの半分を使用することを結論付けることはできません。

PHPでメモリがどのように割り当てられ、どのように使用されるかについては、次の記事を読むことをお勧めします。(ヒント:BIG!)

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. コードを確認してください!!

変数の基本サイズは、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;

変数名の初期化を考慮しない場合、変数が使用する量はすでにわかっています(数値と文字列の場合)。

数字の場合は44バイト

+文字列の場合は24バイト

+文字列の長さ(最後のNUL文字を含む)

(これらの数値は、PHPのバージョンによって異なる場合があります)

メモリアライメントのため、4バイトの倍数に切り上げる必要があります。変数が(関数内ではなく)グローバル空間にある場合、64バイトを割り当てます。

したがって、このページ内のコードの1つを使用する場合は、いくつかの簡単なテストケース(文字列または数値)を使用した結果が、この投稿($ _GLOBAL配列、最初の関数呼び出し、出力バッファ、...)


1
...そして、それがの内部zvalueis_refおよびコピーオンライトに入る前でさえあります。ありがとうございました。
Piskvorは

1
おかげで、PHPマニュアルのそのページを逃しました。私は私の回答を完了するためにリンクを追加しました(しかし、あなたはすでにそれを読んだと思います)。
Adam

2

私にも同様の問題があり、私が使用した解決策は、変数をファイルに書き込んでからfilesize()を実行することでした。おおよそこのように(テストされていないコード):

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

この解決策はディスクIOを伴うため、それほど高速ではありませんが、memory_get_usageのトリックよりもはるかに正確なものを提供するはずです。必要な精度に依存します。


このソリューションは文字列と文字列の1次元配列に対してのみ機能し、その使用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;
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.