2007年9月9日日曜日

PHPの配列によるメモリ消費.

 久々更新.

 前から思っていましたが、何故PHPの配列はあんなにメモリを消費するんだろ.
 大きいデータの場合、文字列→配列へexplodeなりすると、メモリ消費量が一気に10倍程度まで跳ね上がります orz
 この傾向は当然ながらデータサイズが小さい物より大きいものの方がより顕著です.
 迂闊に大きいデータを配列化しようものなら、貴重なメモリ資源が喰い散らかされてしまいます(終了時には解放されますが、Linuxのメモリ最適化処理なんかの環境下だと、終了後も一見掴みっぱなしに見えるから気持ち悪い…).
 例えばデータベース.
 データベース回りの機能をクラス化すると、大抵SELECTクエリの結果データを配列へ格納する様に作ると思いますが、この時も直接mysql_fetch_xxxxで処理を回すより結構なメモリ消費量となってしまいます.
 更に、データベースから取得した配列データのエンコードを変換して出力したいとした時、利便性から「mb_convert_variables」(配列データ中のエンコードを指定のエンコードに一括変換する関数)を使うケースが多いと思いますが、この「mb_convert_variables」も曲者で、処理後のメモリ消費が処理前の倍近くになってしまいます.
 分かり易く書くと↓こんな感じ.

echo "MEMORY0 : " . number_format(memory_get_usage()) . " byte(初期値)<br>";

$strBuffer = str_repeat("ABCDEFG,", 10000);
echo "MEMORY1 : " . number_format(memory_get_usage()) . " byte(文字列生成)<br>";

$aryBuffer = explode(",", $strBuffer);
echo "MEMORY2 : " . number_format(memory_get_usage()) . " byte(配列生成)<br>";

unset($strBuffer);
echo "MEMORY3 : " . number_format(memory_get_usage()) . " byte(文字列解放)<br>";

mb_convert_variables("SJIS-win", "UTF-8", $aryBuffer);
echo "MEMORY4 : " . number_format(memory_get_usage()) . " byte(mb_convert_variables実行)<br>";

unset($aryBuffer);
echo "MEMORY5 : " . number_format(memory_get_usage()) . " byte(配列解放)<br>";

 ※PHP5.2.1以前の場合、「memory_get_usage」は「--enable-memory-limit」付きでメイクする必要があります.

 結果↓

MEMORY0 : 59,160 byte(初期値)
MEMORY1 : 139,568 byte(文字列生成)
MEMORY2 : 1,045,532 byte(配列生成)
MEMORY3 : 965,600 byte(文字列解放)
MEMORY4 : 1,637,344 byte(mb_convert_variables実行)
MEMORY5 : 91,424 byte(配列解放)


 内部で単純に複製して変換しているからなのかもしれませんが、メモリ消費が変換対象の配列変数を解放するまで掴みっぱなしになるのは如何なものかと…

 まぁそんな事言い出すと、全部変数解放した後の消費量も気になるじゃねーか!って話になりますけどw

 こうして考えると、世にあるPHP用Frameworkがそれぞれどの程度のメモリ消費なのか、見比べてみるのも面白いかもしれません.
 同等の機能を実現出来るFrameworkなら、エコなコードの方がいいに決まってますから.

0 件のコメント: