PHP は、特に大量のメモリを消費するため、配列処理が非常に非効率になることがあります。これにより、httpd プロセスが大量のリソースを消費することがよくあります。かつて bkJia Web サイトが一部のコンテンツを呼び出したところ、配列が大きすぎてメモリがオーバーフローし、Apache が頻繁にクラッシュしました。一般に、PHP で多数の配列操作を実行する場合、プログラミング設計の問題がある程度反映される可能性がありますが、配列が占有するメモリを大まかに見積もる必要があります。
まず、1000 要素の整数配列が占めるメモリを感じてください:
echomemory_get_usage() . ”;
$a = 配列();
for ($i=0; $i
$a[$i] = $i + $i;
}
echomemory_get_usage() . ”;
for ($i=1000; $i
$a[$i] = $i + $i;
}
echomemory_get_usage() . ”;
出力は次のとおりです:
58176
162956
267088
1000 要素の整数配列には 100k のメモリが必要で、各要素は平均 100 バイトを占有することが大まかにわかります。純粋な C では全体的に 4k しか必要としません。 Memory_get_usage() によって返される結果は、すべてが配列によって占有されているわけではなく、PHP 自体によって割り当てられたいくつかの構造も含まれています。組み込み関数によって生成された配列は、実際の空間に近い可能性があります。
$a = array_fill(0, 10000, 1);
echo "10k 要素: " .memory_get_usage() "、システム: " .memory_get_usage(true) "
”;
$b = array_fill(0, 10000, 1);
echo "10k 要素: " .memory_get_usage() "、システム: " .memory_get_usage(true) "
”;
入手:
10k エレメント: 724696、システム: 786432
10,000 要素: 1390464、システム: 1572864
この結果から、配列の 1 つの要素は 60 バイト程度しか占有しないことがわかります。 PHP の配列変数にはまず zval 構造体が必要です。
配列の C 構造体を見てみましょう。
zvalue_value 値;
zend_uint refcount__gc;
zend_uchar 型;
zend_uchar is_ref__gc;
};
zvalue_value は共用体です:
長い lval;
ダブル dval;
構造体{
char *val;
int len;
} str;
ハッシュテーブル *ht;
zend_object_value obj;
zvalue_value;
通常、zval 構造には 8+6=14 バイトが必要です。PHP の各変数には対応する zval が必要ですが、配列、文字列、オブジェクトには追加のストレージ構造も必要であり、配列は HashTable です:
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
バケット *pInternalPointer;
バケット *pListHead;
バケット *pListTail;
バケット **arBuckets;
dtor_func_t pDestructor;
zend_bool 永続的;
unsigned char nApplyCount;
zend_bool bApplyProtection;
}ハッシュテーブル;
HashTable 構造には 40 バイトが必要で、各配列要素は Bucket 構造に保存されます:
うろんh;
uint nKeyLength;
void *pData;
void *pDataPtr;
構造体バケット *pListNext;
構造体バケット *pListLast;
構造体バケット *pNext;
構造体バケット *pLast;
char arKey[1];
バケット;
Bucket 構造体には 36 バイトが必要で、キー長が 4 バイトを超える部分は Bucket の末尾に追加され、要素の値は zval 構造体になる可能性が高く、さらに各配列には Bucket ポインターが割り当てられます。 arBuckets が指す配列。ただし、追加要素ごとにポインタが必要であるとは言えませんが、状況はさらに悪化する可能性があります。これは、1 つの配列要素が 54 バイトを占有することを計算します。これは、上記の推定値からそれほど遠くありません。
空の配列は、変数として少なくとも 14(zval) + 40(HashTable) + 32(arBuckets) = 86 バイトを占め、シンボル テーブル内に位置が必要であり、配列要素でもあるため、空の配列となります。変数の記述と格納には 118 バイトが必要です。スペースの観点から見ると、小さな配列の方が平均してコストが高くなります。もちろん、スクリプトが多数の小さな配列で埋め尽くされることはなく、より少ないスペースコストでプログラミングの利便性が得られます。
しかし、配列をコンテナとして使用する場合は別の話になります。実際のアプリケーションでは、多くの要素を含む多次元配列が頻繁に発生します。たとえば、10k 要素の 1 次元配列は約 540k のメモリを消費しますが、10k の 2 次元配列は実際には 23M を消費し、小さな配列には実際には価値がありません。