2015-01-11 15 views
15

Sto cercando di capire come PHP carica gli array in memoria e quando passa un array consuma memoria.Come funziona la gestione dell'utilizzo della memoria dell'array di PHP?

Così ho questa piccola porzione di codice in esecuzione: Si noti che la matrice di ingresso è meno importante in questo esempio:

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 
echo $this->getMemoryUsage(); 

Questo consuma esattamente 250 KB di memoria, questo significa che la matrice è di circa 250 kB in dimensione, approssimativamente.

così mi sono imbattuto il seguente codice:

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 

$arr[0]['id'] = 'changing this value'; 

$foo = $arr; 
$foo[2]['id'] = 'changing this value again'; 

$bar = $foo; 
$bar[4]['id'] = 'changing this value again and again'; 

$far = $bar; 
$far[5]['id'] = 'changing this value again and again and again'; 

echo $this->getMemoryUsage(); 

Secondo quello che ho letto e mi fu detto, PHP in realtà non copia l'array, fa riferimento solo la matrice originale, ma una volta un cambiamento è reso PHP deve copiare l'intero array.

Immaginate la mia sorpresa quando il codice precedente consuma esattamente 500 kB di RAM.

Qualcuno può spiegare cosa sta succedendo qui?

Giusto per essere chiari, tutti questi indici (0-5 e id) esistono già nell'array originale, sto solo modificando il valore. Il valore originale è un numero intero.

EDIT

Proprio per cancellare il coinvolgimento di $ this-> risultato(); Qui è un altro test che ho condotto:

echo $this->getMemoryUsage(); 
    $arr = $query->result_array(); // array of arrays from codeigniter 
//$arr[0]['id'] = 'changing this value'; 

    $foo = $arr; 
    $foo[2]['id'] = 'changing this value again'; 

    //$bar = $foo; 
    //$bar[4]['id'] = 'changing this value again and again'; 
    // 
    //$far = $bar; 
    //$far[4]['id'] = 'changing this value again and again and again'; 

    echo $this->getMemoryUsage(); 

Questa volta l'uscita è esattamente 250 kB - Proprio come il processo originale senza alcuna modifica

EDIT # 2

Come richiesto, ho' ve correva il codice da qui in poi la mia messa a punto, per assicurarsi che i risultati sono coerenti: http://pastebin.com/cYNg4cg7

Questi sono i risultati:

012.351.641,061 mila

DICHIARAZIONE: 4608 kB
FINALE: 8904 kB
DIFF ALLA DICHIARAZIONE: 4296 kB

Così, anche se la dichiarazione era 4608 e la matrice è stata approvata e ha cambiato 4 volte, è ancora solo meno che raddoppiato la memoria orma.

EDIT # 3

Ho ho fatto funzionare i cambiamenti di memoria dopo ogni attribuzione:

DICHIARAZIONE: 5144 kB
assegnazione A0 aggiunto: 144 kB
assegnazione A1 ha aggiunto: 1768 kB
A2 allocazione aggiunta: 1768 kB
allocazione A3 aggiunto: 1768 kB
FINALE: 10744 kB
DIFFILE DICHIARAZIONE: 5600 kB

Ogni operazione successiva alla prima costa esattamente la stessa cosa, che sembra indicare che la stessa esatta dimensione viene copiata. Questo sembra supportare la risposta di Austin, L'unica cosa che non si aggiunge ora è la dimensione che è stata assegnata, ma questa è una domanda diversa.

Sembra che Austin sia sulla palla, lo accetterò se non arriva altra risposta.

+0

domanda molto difficile, si può essere interessato al seguente articolo che ho letto qualche giorno fa: https: // Nikic .github.io/2011/12/12/How-big-are-PHP-array-really-Hint-BIG-I.html – Fleshgrinder

+0

Ho letto quell'articolo alcune settimane fa, È onestamente fasci nating, ma non spiega come funziona esattamente la copia. – Patrick

+2

Lo so, ho pensato che ti sarebbe piaciuto. Non posso rispondere alla tua domanda e non posso fornirti un link che possa rispondere alla tua domanda. Invece ho recitato la tua domanda in modo da poterla seguire e darle una taglia se non viene pubblicata alcuna risposta, dal momento che mi piacerebbe anche saperlo. :) – Fleshgrinder

risposta

4

Ecco cosa penso sta succedendo:

array PHP sono copy on write come dici tu, ma ogni livello di un array multi-dimensionale è separatamente copy on write. PHP è molto intelligente nel riutilizzare parti di un array multidimensionale e non solo nel suo complesso. (Questo è simile ad alcuni sistemi di file che supportano istantanee, come ZFS.)

Esempio: dire che abbiamo questo array

$x = array('foo' => array(1, 2, 3), 'bar' => array(4, 5, 6)); 

Questo è memorizzato nella memoria non come un unico pezzo, ma come pezzi separati qui etichettato A, B, C, e $x:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 

Ora ti permette di fare una copia di $x:

$y = $x; 

Questo utilizza poca memoria in più, perché tutto quello che deve fare è creare un altro puntatore a C:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 
{pointer to C} //$y 

Ora lascia cambiamento $y:

$y['foo'][0] = 10; 

Ecco cosa non accade :

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array(4, 5, 6) //B2 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B2}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

Avviso che B e B2 sono identici. Non c'è alcuna necessità di mantenere la stessa cosa due volte, in modo da ciò che effettivamente accade è questo:

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

In questo semplice caso, il vantaggio è piuttosto piccolo, ma immaginate che invece di tre numeri, la matrice 'bar' conteneva migliaia di numeri . Finisci per risparmiare enormi quantità di memoria.

In relazione al codice originale, provare a stampare l'utilizzo della memoria non solo all'inizio e alla fine, ma dopo ogni nuova assegnazione di array.Vedrai che l'utilizzo della memoria aumenta solo di una frazione di ciò che l'array originale occupa dopo ogni passaggio. Questo perché solo una parte dell'array viene copiata, non l'intera cosa. Nello specifico, l'array di primo livello e lo specifico sub array che si modifica vengono copiati, ma gli altri sub-array non vengono copiati.

Il fatto che la quantità finale di memoria utilizzata sia il doppio di quella iniziale sembra essere una coincidenza dovuta alla particolare configurazione del codice e al numero di copie dell'array che si creano.

(In realtà, PHP può fare anche meglio di quello che descrivo qui (probabilmente conserverà solo una copia di 'foo' e 'bar', ecc.), Ma per la maggior parte si riduce allo stesso tipo di trucco.)

Se volete una dimostrazione più drammatica di questa, fare qualcosa di simile:

$base = memory_get_usage(); 
$x = array('small' => array('this is small'), 'big' => array()); 
for ($i = 0; $i < 1000000; $i++) { 
    $x['big'][] = $i; 
} 
echo (memory_get_usage() - $base).PHP_EOL; //a lot of memory 
$y = $x; 
$y['small'][0] = 'now a bit bigger'; 
echo (memory_get_usage() - $base).PHP_EOL; //a bit more memory 
$z = $x; 
$z['big'][0] = 2; 
echo (memory_get_usage() - $base).PHP_EOL; //a LOT more memory 
+0

Ehi, stavo pensando qualcosa in questa direzione, tuttavia non si sommava, Usando un altro caso di test era già stato riprodotto l'allocazione di memoria raddoppia dopo 4 azioni identiche (vedi l'ultima modifica nella domanda). 4 azioni identiche su array della stessa dimensione dovrebbero avere un effetto lineare. Se facciamo esattamente la stessa azione 4 volte, una singola volta dovrebbe costare 1/4 del costo. Si noti che nei test continuo a cambiare diversi indici, se ne copia uno, copiamo tutto. Modificherò la mia domanda con nuovi valori di memoria non appena posso. – Patrick

+2

@Patrick La prima azione non costa tanto, perché non è necessario mantenere il vecchio valore poiché nessuna variabile lo utilizza. Le azioni dalla seconda alla quarta devono fare una copia perché l'originale è ancora in uso.Stampa l'utilizzo della memoria dopo ogni incarico per vederlo. – Austin

+0

Dopo altri test la tua risposta è davvero corretta, Superba! :) – Patrick

Problemi correlati