2009-04-13 9 views
12

Se sto ospitando un'applicazione a lunga esecuzione come un server web all'interno di un'immagine Common Lisp, quale strategia devo usare per gestire il garbage collector?In un'applicazione Common Lisp con esecuzione prolungata, quale strategia deve essere utilizzata per gestire i rifiuti?

Suppongo che, per impostazione predefinita, il garbage collector abbia il diritto di dedicare lunghi periodi di tempo all'ordinamento dell'heap, a volte non è possibile prevederlo. Ciò potrebbe influire su una particolare richiesta del browser in modi che non desidero.

C'è un metodo in Common Lisp per controllarlo? Forse incoraggiandolo a lavorare in un modo "piccolo e spesso"?

+0

Quale implementazione di lisp stai usando? – Zifre

+0

Quando dici "Sto assumendo", sembra che tu stia cercando di fare ottimizzazioni senza aver fatto alcuna profilazione. Stai riscontrando problemi specifici che devono essere risolti nel tuo ambiente? – Ken

+0

Sto iniziando il processo di informarmi sui problemi che devono ancora apparire. Data la quantità di linguaggi GC in uso sui servizi Web, presumo che si tratti di un problema ampiamente risolto. –

risposta

24

Diverse implementazioni Lisp hanno eccellenti Garbage Collector. Un problema particolare è che le applicazioni Lisp hanno spesso un alto tasso di allocazione di oggetti piccoli (consi, ...).

Ci sono alcune cose da considerare.

  • GC preciso e conservativo. Non sono un grande fan dei conservatori GC (Boehm, ecc.) Per Lisp. Un problema dei GC conservativi è che non trovano tutti i rifiuti. Questo può essere un problema per i programmi di lunga durata e portare alla frammentazione e alla memoria non utilizzata non recuperata. GC precisi utilizzano le informazioni sui tag dei dati Lisp e possono identificare ogni tipo di dati di ogni oggetto. I GC conservativi sono stati inventati per implementazioni linguistiche di programmazione che non utilizzano dati con tag (C++, ...).

  • copia GC, compattazione GC. Per lavorare contro la frammentazione della memoria nei Lisp a lunga esecuzione, un GC che compatta e localizza gli oggetti può essere utile. A volte si presenta un problema quando è necessario eseguire il rehashed degli hashtables (perché la posizione cambia). Un GC di copia potrebbe richiedere più memoria (perché c'è un da e uno spazio di memoria). Ma quando il GC copia gli oggetti da uno spazio di memoria a un altro, lo rende automaticamente più compatto. GC più avanzati (come quelli sulla macchina Lisp) possono anche ordinare oggetti e allocare oggetti dello stesso tipo l'uno vicino all'altro, supponendo che ciò acceleri l'accesso agli oggetti.

  • GC effimero. Ciò significa che esiste un primo stage GC che viene eseguito esclusivamente nella memoria principale e riceve supporto da un'unità di gestione della memoria per identificare le aree di memoria modificate. La scansione della memoria principale è veloce rispetto alla scansione della memoria virtuale e la scansione delle sole regioni di memoria modificate riduce ulteriormente la quantità di lavoro. Quando vengono allocati molti oggetti e si trasformano rapidamente in rifiuti, questo fornisce pause GC molto brevi.

  • GC generazionale. Solitamente i GC al giorno d'oggi sono generazionali. C'è più di una generazione e gli oggetti che sopravvivono a qualche GC sono promossi a una generazione più anziana. Di solito solo la prima generazione è GCed molto spesso.

  • Sintonizzazione. I GC di, diciamo, LispWorks e Allegro CL hanno molte manopole di sintonia. Soprattutto per le applicazioni di lunga durata, ha senso leggere il manuale e ad esempio sintonizzare il numero di generazioni, le loro dimensioni e altre cose.

  • memoria virtuale. GC su memoria virtuale è di solito molto lento.Evita che, se possibile, aggiungi più RAM alle macchine.

  • gestione memoria manuale. Ad esempio, il server Web CL-HTTP esegue una gestione manuale della memoria utilizzando le risorse . Si tratta di pool di oggetti pre-allocati che possono essere reinizializzati molto rapidamente. Le Lisp Machines lo usavano molto. L'uso tipico di questi è nei buffer di lettura per i flussi. Invece di creare nuove stringhe con ogni operazione di lettura, è utile utilizzare i buffer riutilizzabili.

  • assegnazione dello stack. Alcuni Common Lisp consentono l'allocazione dello stack di alcuni dati - lasciando il blocco quindi libera automaticamente la memoria. Ciò presuppone quindi che la memoria non sia più referenziata in uscita da un blocco.

  • GC concomitante. Nessuna delle solite implementazioni Common Lisp ha un concomitante supporto GC AND per i thread Lisp simultanei. Alcune implementazioni hanno thread Lisp simultanei, ma il GC li fermerà tutti mentre sta funzionando.

  • creazione del profilo del GC. Se non si è sicuri su dove si svolga l'allocazione e su cosa fa il GC, è necessario scoprirlo utilizzando le informazioni di profilazione.

Se il Lisp ha un GC generazionale preciso che viene eseguito nella memoria principale, è difficile avere problemi con lunghe pause. Clozure CL (un'implementazione Common Lisp gratuita), ad esempio, ha un'ottima implementazione del GC. Si desidera evitare la frammentazione della memoria e le raccolte di dati inutili nella memoria virtuale. Se necessario, utilizzare un'implementazione Lisp a 64 bit con più memoria principale.

Puntatori:

Si può vedere dalla documentazione LispWorks e Allegro CL ha un sacco di manopole per la messa a punto GC.

Common Lisp ha alcune funzioni che riguardano l'ambiente di implementazione. (ROOM) è una funzione che offre una panoramica dell'utilizzo della memoria. (ROOM t) fornisce maggiori dettagli (qui LispWorks):

CL-USER 2 > (room t) 
Generation 0: Total Size 1823K, Allocated 1090K, Free 725K 
      Segment 2008AAB8: Total Size 507K, Allocated 361K, Free 142K 
        minimum free space 64K, 
         Awaiting promotion = 0K, sweeps before promotion =10 
      Segment 217E7050: Total Size 1315K, Allocated 729K, Free 582K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =2 
Generation 1: Total Size 1397K, Allocated 513K, Free 871K 
      Segment 20CB9A50: Total Size 68K, Allocated 48K, Free 15K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =4 
      Segment 216D7050: Total Size 1088K, Allocated 331K, Free 752K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =4 
      Segment 2004E4F8: Total Size 241K, Allocated 133K, Free 103K 
        minimum free space 0K, static 
Generation 2: Total Size 2884K, Allocated 1290K, Free 1585K 
      Segment 21417050: Total Size 2816K, Allocated 1227K, Free 1584K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =4 
      Segment 20DA5DA0: Total Size 68K, Allocated 62K, Free 1K 
        minimum free space 117K, 
         Awaiting promotion = 0K, sweeps before promotion =4 
Generation 3: Total Size 19373K, Allocated 19232K, Free 128K 
      Segment 20109A50: Total Size 11968K, Allocated 11963K, Free 0K 
        minimum free space 3K, 
         Awaiting promotion = 0K, sweeps before promotion =10 
      Segment 20DB6E18: Total Size 6528K, Allocated 6396K, Free 128K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =10 
      Segment 20CCAAC8: Total Size 876K, Allocated 872K, Free 0K 
        minimum free space 0K, 
         Awaiting promotion = 0K, sweeps before promotion =10 

Total Size 25792K, Allocated 22127K, Free 3310K 
+0

Non penso che la tua dichiarazione sull'uso dei tag sia completamente corretta. Un GC preciso deciderà effettivamente come effettuare la pulizia guardando il tag di un oggetto (come lo sarà l'impreciso), ma prima deve sapere se il contenuto di un registro è un puntatore di oggetto o un valore immediato. Quest'ultima operazione è ciò che distingue i GC precisi da quelli imprecisi. – skypher

+0

Scatta una serie di cose diverse: oggetti immediati (numeri, caratteri, ...) e oggetti non immediati (l'array contiene un puntatore a loro: matrici, strutture, stringhe, ...).In che modo il GC sa che una cosa è un numero e l'altra è un puntatore (a una struttura)? Il GC conservativo è stato sviluppato per linguaggi come C e C++ in cui non sono presenti oggetti con tag. Il GC determina euristicamente cosa potrebbe essere un puntatore e cosa no. –

+0

Potrebbe essere stato sviluppato per linguaggi senza tag come C, ma è utilizzato anche in alcuni ambienti Lisp oggi, ad esempio, CMUCL e SBCL utilizzano un GC conservativo su architetture "register poor" come x86: http: //sbcl-internals.cliki. net/GENCGC – Ken

2

La raccolta dei rifiuti ha fatto molta strada sin dai primi giorni e si è lavorato molto per evitare l'attesa lunga e imprevedibile. Per le implementazioni moderne, penso che quelle siano una cosa del passato.

Tuttavia, i dettagli della garbage collection variano in base all'implementazione. Non ci sono molte implementazioni Lisp di alta qualità, quindi non dovresti avere difficoltà a consultare la loro documentazione sulla garbage collection.

Problemi correlati