2013-06-18 12 views
39

Ruby 2.0 introduce un garbage collector amichevole copia-su-scrittura. I miei processi non sembrano mantenere la memoria condivisa per più di pochi minuti: sembra spostarsi abbastanza rapidamente da shared_dirty a private_dirty.Come migliorare la condivisione della memoria tra i processi unicorno con Ruby 2.0 su Linux

Alcuni altri hanno avuto successo nel far funzionare questo lavoro:

Questo programma può essere utilizzato per controllare le statistiche di memoria su Linux: https://gist.github.com/kenn/5105061

mio unicorno configurazione: https://gist.github.com/inspire22/f82c77c0a465f1945305

Per qualche motivo le mie app per unicorno, anche con preload_app = true, hanno una memoria molto meno condivisa. Rubino 2.0-P195, Rails 3.2, Linux 2.6.18 (CentOS)

[[email protected] script]# ruby memstats.rb 4946 
Process:    4946 
Command Line:  unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D 
Memory Summary: 
    private_clean     0 kB 
    private_dirty    56,324 kB 
    pss      60,256 kB 
    rss      83,628 kB 
    shared_clean    4,204 kB 
    shared_dirty    23,100 kB 
    size      108,156 kB 
    swap       68 kB 

Se l'arresto del processo di maestro del tutto (non solo un HUP) quindi riavviare e controllare immediatamente un lavoratore prima di qualsiasi richieste sono in coda, ho avere una storia migliore:

[[email protected] script]# ruby memstats.rb 5743 
Process:    5743 
Command Line:  unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D 
Memory Summary: 
    private_clean     0 kB 
    private_dirty    21,572 kB 
    pss      27,735 kB 
    rss      66,296 kB 
    shared_clean    2,484 kB 
    shared_dirty    42,240 kB 
    size      91,768 kB 
    swap       0 kB 

Ma nel giro di 5 secondi di messa in esercizio, sono tornati a ~ 20MB di shared_clean + shared_dirty.

Ho il sospetto che lo swapping potrebbe causare il problema, ma dopo aver diminuito lo swappiness e assicurandomi che né i processi padre né figlio vengano scambiati (utilizzando swapstats.rb), il problema persiste.

Non capisco esattamente quale sia la memoria shared_dirty e come venga trasformata in memoria privata. Mi piacerebbe anche suggerimenti per migliorare la longevità e la quantità della mia memoria condivisa. Grazie!

+0

Non ho ancora una soluzione a questo. Ora credo che sia un problema di a) versione a 32 bit di Linux, e b) un ambiente con vincoli di memoria (anche se con swappiness abbassato si verifica ancora) – Kevin

+0

Ho ricevuto questo commento dall'autore del gist: https: // twitter.com/kenn/status/402832587007086592 –

+0

fantastico, grazie :) – Kevin

risposta

7

Secondo this answer, che si può avere già visto, c'è una riga che legge:

Nota che una pagina "share-grado" viene conteggiato come una mappatura privata fino a quando non è in realtà condivisa. Ad esempio, se esiste attualmente un solo processo che utilizza la libagion , la sezione di testo della libreria apparirà nei mapping privati ​​del processo . Sarà conteggiato nei mapping condivisi (e rimosso da quelli privati) solo se/quando un altro processo inizia utilizzando quella libreria.

Quello che vorrei fare per verificare se stai ricevendo i benefici outlined in this article, è messo un file XML di 10MB come una stringa letterale direttamente nel codice sorgente. Quindi, se accendi 20 lavoratori, sarai in grado di vedere se stai utilizzando 200 MB di memoria, o solo 10 MB, come previsto con la nuova funzionalità di garbage collection.

UPDATE:

stavo guardando attraverso the unicorn source e ha trovato un riferimento a this wonderful article.

In sintesi, si afferma che, al fine di adattare le applicazioni per sfruttare Ruby Enterprise Edition copy-on-write amichevole garbage collector, è necessario set GC.copy_on_write_friendly su true prima di sborsare.

if GC.respond_to?(:copy_on_write_friendly=) 
    GC.copy_on_write_friendly = true 
end 

In base al file di configurazione unicorn fornito, sembra che manchi dell'assegnazione.

Inoltre, mi è piaciuto leggere questi articoli correlati:

Secondo il fork man page:

Sotto Linux, fork() è implementato usando le pagine copy-on-write, quindi la penalità solo è il tempo e la memoria richiesti per duplicare le tabelle della pagina padre e per creare una struttura unica per il bambino .

Dalla versione 2.3.3, anziché invocare fork() Sistema chiamata del kernel, la forcella glibc() avvolgitore che viene fornito come parte del NPTL filettatura attuazione richiama clone (2) con flag che forniscono lo stesso effetto della tradizionale chiamata di sistema. (Un call to fork() è equivalente a una chiamata a clone (2) che specifica flag come solo SIGCHLD.) Il wrapper glibc richiama tutti i gestori di fork che sono stati stabiliti utilizzando pthread_atfork (3).

E secondo la clone man page:

differenza forcella (2), queste chiamate consentono il processo figlio per condividere parti di suo contesto di esecuzione con il processo chiamante, come lo spazio di memoria , la tabella dei descrittori di file e la tabella dei gestori di segnale .

Quindi, sto leggendo questo per significare: forcella copy-on-write di Linux, che è la caratteristica che si basa su unicorno per implementare la condivisione della memoria, non è stato attuato fino libc 2.2.3 (per favore, qualcuno corretta me se sbaglio in questa interpretazione).

Per verificare quale versione di libc si sta eseguendo, è possibile digitare:

ldd --version 

Oppure, trovare glibc ed eseguirlo direttamente. Sul mio sistema ha trovato il file nel seguente percorso:

locate libc.so 
/lib/x86_64-linux-gnu/libc.so.6 
+0

Interessante idea di prova, grazie! In teoria, il programma memstats.rb di cui faccio il link sopra dovrebbe spiegarlo, ma non si sa mai. – Kevin

+0

Più lo guardo, questa è una domanda piuttosto brutta in quanto è "open ended". Senza l'accesso a un sistema sarebbe impossibile "inchiodare" una risposta. La tua risposta offre un ottimo modo per eseguire il debug di ciò che potrebbe andare storto. In definitiva, sospetto che abbia qualcosa a che fare con una versione precedente di linux su un sistema a 32 bit, con alcuni limiti di memoria/swapping moderati. Il tuo test è una grande idea, sebbene possa essere duplicato con lo script memstats.rb. – Kevin

+0

@Kevin Aggiunto un aggiornamento che sembra essere la causa del problema. Per favore fatemi sapere se funziona per voi. – Homer6

Problemi correlati