次のアプローチではどのくらいのメモリが使用されますか?
list($appid,$openid) = ["testcontent","test"];
$m0 = memory_get_usage();$k = range(1,200000);$m1 = memory_get_usage();echo round(($m1-$m0)/pow(1024,2),4) ."MB\n";foreach ($k as $i){ $n1 = "kk$i"; $n2 = "tt$i"; list($$n1,$$n2) = [$i,$i*3];}$m2 = memory_get_usage();echo round(($m2-$m1)/pow(1024,2),4) ."MB\n";$m1 = memory_get_usage();foreach ($k as $i){ $n1 = "kk$i"; $n2 = "tt$i"; $$n1 = $i+time(); $$n2 = 2*time();}$m2 = memory_get_usage();echo round(($m2-$m1)/pow(1024,2),4) ."MB\n";
上記の操作の出力は次のとおりです。
27.9404MB51.3041MB9.1553MB
配列によって占有されているメモリが通常割り当てられるコンテンツよりもはるかに大きい
PHP では、数値を表すために Long 型が使用され、int 型は使用されません。 PHP は変数の型を区別せず、int float char * などの概念が存在しない、弱い型指定言語であることは誰もが理解しています。 PHP が zend に保存する変数を見てみましょう。PHP の各変数には、対応する zval があり、その構造は次のとおりです:
typedef struct _zval_struct zval; struct _zval_struct { /* Variable information */ zvalue_value value; /* The value 1 12字节(32位机是12,64位机需要8+4+4=16) */ zend_uint refcount__gc; /* The number of references to this value (for GC) 4字节 */ zend_uchar type; /* The active type 1字节*/ zend_uchar is_ref__gc; /* Whether this value is a reference (&) 1字节*/ };
PHP は UNION を使用します。構造体は変数の値を格納するために使用されます。つまり、zvalue_value は共用体です。UNION 変数によって占有されるメモリは、最大メンバー データ スペースによって決まります。
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { /* string value */ char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; /*object value */ } zvalue_value;
メンバーの最大データ領域は struct str で、ポインターは *val に 4 バイト、INT は 4 バイトの合計 8 バイトを占めます。 struct zval が占めるスペースは 8+4+1+1 = 14 バイトです。実際、zval の配列、文字列、オブジェクトには追加のストレージ構造が必要です。
HashTable 構造が定義されています。 Zend/zend_hash.h.
typedef struct _hashtable { uint nTableSize;//4 uint nTableMask;//4 uint nNumOfElements;//4 ulong nNextFreeElement;//4 Bucket *pInternalPointer; /* Used for element traversal 4*/ Bucket *pListHead;//4 Bucket *pListTail;//4 Bucket **arBuckets;//4 dtor_func_t pDestructor;//4 zend_bool persistent;//1 unsigned char nApplyCount;//1 zend_bool bApplyProtection;//1 #if ZEND_DEBUG int inconsistent;//4 #endif } HashTable;
HashTable 構造には 39 バイトが必要で、各配列要素は Bucket 構造に格納されます。
typedef struct bucket { ulong h; /* Used for numeric indexing 4字节 */ uint nKeyLength; /* The length of the key (for string keys) 4字节 */ void *pData; /* 4字节*/ void *pDataPtr; /* 4字节*/ struct bucket *pListNext; /* PHP arrays are ordered. This gives the next element in that order4字节*/ struct bucket *pListLast; /* and this gives the previous element 4字节 */ struct bucket *pNext; /* The next element in this (doubly) linked list 4字节*/ struct bucket *pLast; /* The previous element in this (doubly) linked list 4字节*/ char arKey[1]; /* Must be last element 1字节*/ } Bucket;
Bucket 構造には 33 バイトが必要です。 4 バイトを超えるキー長の部分は Bucket の末尾に追加され、要素の値は zval 構造体になる可能性が高くなります。さらに、各配列には arBuckets が指す Bucket ポインターの配列が割り当てられます。要素が追加されるたびにポインターが必要であるとは言えませんが、状況はさらに悪化する可能性があります。これは、1 つの配列要素が 54 バイトを占有することを計算し、これは上記の見積もりとほぼ同じです。
空の配列は、変数として少なくとも 14(zval) + 39(HashTable) + 33(arBuckets) = 86 バイトを占め、シンボル テーブル内に位置が必要であり、配列要素でもあります。したがって、空の配列変数の記述と格納には 118 バイトが必要です。スペースの観点から見ると、小さな配列の方が平均してコストが高くなります。もちろん、スクリプトが多数の小さな配列で埋め尽くされることはなく、より少ないスペースコストでプログラミングの利便性が得られます。しかし、配列をコンテナとして使用する場合は別の話になります。実際のアプリケーションでは、多くの要素を含む多次元配列が頻繁に発生します。たとえば、10k 要素の 1 次元配列は約 540k のメモリを消費しますが、10k の 2 次元配列は実際には 23M を消費し、小さな配列には実際には価値がありません。
PHP 配列が占有するメモリサイズの解析