2011-10-05 5 views
6

Ho uno script php che utilizza Doctrine2 e Zend per calcolare alcune cose da un database e inviare alcune e-mail per 30.000 utenti.C'è un modo per sapere quali oggetti e quanti ne ho in memoria?

Il mio script sta perdendo memoria e voglio sapere quali sono gli oggetti che stanno consumando quella memoria, e se è possibile chi sta mantenendo un riferimento a loro (quindi non permettendo loro di essere rilasciati).

Im usando PHP 5.3.x, quindi i riferimenti circolari pianura non dovrebbe essere il problema.

Ive ha provato a utilizzare le funzionalità di traccia xdebug per ottenere mem_delta senza successo (troppi dati).

Ive provato manualmente aggiungendo memory_get_usage prima e dopo le funzioni importanti. Ma l'unica conclusione che ho ottenuto è stata la perdita di circa 400k per utente e di 3000 utenti che mi danno il 1 GB che ho a disposizione.

Ci sono altri modi per conoscere dove e perché la memoria sta perdendo? Grazie

+1

Bene, gli utenti dovrebbero essere processati uno dopo l'altro, ci dovrebbero essere solo 400k di memoria necessari! Se ogni ciclo aumenta l'utilizzo della memoria, qualcosa nel tuo design è seriamente sbagliato! – markus

+0

Bene, ho un ciclo che chiama una funzione che fa quanto segue: Ottieni le informazioni per l'utente, calcola (con la memorizzazione inclusa), invia posta, rilascia risorse. E ogni utente è indipendente l'uno dall'altro, quindi le risorse non vengono rilasciate –

+0

Hai dato un'occhiata al gestore di entità di doctrine? Non ho molta familiarità con la dottrina ma potrebbe eventualmente memorizzare riferimenti a entità/proxy/... per tutti gli utenti 30k. – Fge

risposta

2

Si potrebbe provare a inviare dire 10 e-mail e poi l'inserimento di questo

get_defined_vars(); 

http://nz.php.net/manual/en/function.get-defined-vars.php

Alla fine dello script o dopo l'e-mail viene inviata (a seconda di come il codice è messa a punto) .

Questo dovrebbe dirvi cosa è ancora caricato, e cosa si può disinserire/trasformarsi in un punto di riferimento.

Inoltre, se ci sono due cose caricate, questo è vicino all'avvio e alla fine del codice e determina la differenza.

+0

Grazie, sembra utile. Ci proverò nel mio ciclo. Dalla documentazione, l'unica cosa che mi preoccupa è che mi dà solo informazioni sull'oggetto in ambito. E sto indovinando che il problema di memoria si interrompe perché è fuori portata. –

0

Questo non è uno strumento che vi darà quello che ti serve, ma forse ti aiuterà. Se non lo sei già, puoi implementare il pattern della mappa dell'identità in cui ogni volta che crei un oggetto viene registrato con la mappa dell'identità in modo che in qualsiasi momento puoi chiamare l'IM e vedere quali oggetti sono caricati o dirgli di scaricare qualsiasi oggetti caricati.

http://martinfowler.com/eaaCatalog/identityMap.html

+0

@Joey_Rivera Quel modello è già implementato da doctrine, e lo userei se avessi bisogno di prestazioni nei tempi di interrogazione, il mio problema è il contrario. Non ho bisogno di aggiungere più riferimenti agli oggetti, ho bisogno di un modo per ridurre il conteggio dei riferimenti. –

2

30,000 oggetti per idratare è un bel po '. Doctrine 2 è stabile, ma ci sono alcuni bug, quindi non sono troppo sorpreso dai problemi di perdita di memoria.

Anche se con set di dati più piccoli ho avuto un discreto successo utilizzando le dottrine batch processing e creando un risultato iterabile.

È possibile utilizzare il codice dagli esempi e aggiungere un gc_collect_cycles() dopo ogni iterazione. Devi testarlo, ma per me le dimensioni dei lotti di circa 100 hanno funzionato abbastanza bene - quel numero ha dato un buon equilibrio tra prestazioni e utilizzo della memoria.

È molto importante che lo script riconosca quali entità sono state elaborate in modo che possano essere riavviate senza problemi e riprendere il normale funzionamento senza inviare due e-mail.

$batchSize = 20; 
$i = 0; 
$q = $em->createQuery('select u from MyProject\Model\User u'); 
$iterableResult = $q->iterate(); 
while (($row = $iterableResult->next()) !== false) { 
    $entity = $row[0]; 

    // do stuff with $entity here 
    // mark entity as processed 

    if (($i % $batchSize) == 0) { 
     $em->flush(); 
     $em->clear(); 

     gc_collect_cycles(); 
    } 
    ++$i; 
} 

In ogni caso, forse si dovrebbe ripensare l'architettura per quello script un po ', come un ORM non è adatto per l'elaborazione di grandi quantità di dati. Forse puoi cavartela lavorando sulle prime righe SQL?