2015-02-02 19 views
10

Sono in fase di sviluppo di un'applicazione Ruby on Rails. Sono un novizio di Ruby/Rails. Io uso Ruby 2.2.0 e Rails 4.2. Quando eseguo un comando come:Ruby: Impossibile allocare memoria

rails g migration SomeMigrationName 

non riesce con il

Cannot allocate memory - fork(2) (Errno::ENOMEM) 

Io uso MacBook Pro metà 2014 con OS X 10.10 a bordo e Vagrant/Virtualbox per eseguire una macchina virtuale (Ubuntu 14.04) per lo sviluppo di Rails.

Ecco il mio file di Vagrant:

Vagrant.configure(2) do |config| 
    config.vm.box = "ubuntu/trusty64" 
    config.vm.network "forwarded_port", guest: 3000, host: 3000 
    config.vm.synced_folder "dev", "/home/vagrant/dev" 
    config.vm.synced_folder "opt", "/opt" 
    config.vm.provider "virtualbox" do |vb| 
    vb.memory = "512" 
    end 
end 

Ho letto che un tale errore si verifica quando la RAM è fuori limite, ma io uso lo stesso config (file Vagrant) per l'altro ambiente dev, che gestisce diversi Applicazioni Python/Tornado, MongoDB e Redis e tutto funziona perfettamente.

Devo aumentare il valore vb.memory o è un bug di Ruby?

+0

È difficile dire se è necessaria più RAM. In generale, un'app per rails di medie dimensioni può utilizzare 512M. Rails carica l'intera applicazione in memoria x tuttavia molti server/processi in corso. In genere ho impostato la RAM a 2 GB per tentare anche. Tuttavia, faccio il mio sviluppo sul mio Mac in generale, non su una Virtual Box. –

risposta

23

Quando Ruby chiama il numero fork, il sistema operativo eseguirà una copia dell'intero spazio di indirizzamento dei processi principali, anche se il fork viene chiamato solo a exec un altro piccolo processo come ls. Momentaneamente, il tuo sistema deve essere in grado di allocare un blocco di memoria almeno della dimensione dell'origine genitore di Ruby prima di ridurlo a ciò di cui il processo figlio ha effettivamente bisogno.

Quindi i binari sono generalmente abbastanza affamati di memoria. Quindi se qualcosa usa fork, è necessario il doppio della memoria.

TL; DR Utilizzare posix-spawn invece di fork se si ha il controllo del codice. In caso contrario, dare la vostra VM 1024MB o un po 'di spazio in più di swap per recuperare il gioco per il fork chiamata

Uso


Esempio Rubino memoria con fork

Prendere una macchina virtuale a caso, questo ha lo spazio di swap disabili:

$ free -m 
      total  used  free  shared buffers  cached 
Mem:   1009  571  438   0   1   35 
-/+ buffers/cache:  534  475 
Swap:   0   0   0 

Guardate la fila Mem: e free colonna. Si tratta di circa circa il limite di dimensione per un nuovo processo, nel mio caso 438 MiB

mio buffers/cached sono già stati flushed per questo test in modo che la mia memoria free è al suo limite. Potrebbe essere necessario prendere in considerazione i valori buffers/cache se sono grandi. Linux ha la capacità di rimuovere la cache non aggiornata quando la memoria è necessaria per un processo.


utilizzare una parte della memoria

creare un processo rubino con una corda intorno al formato della vostra memoria libera. C'è un po 'di overhead per il processo ruby quindi non corrisponderà esattamente allo free.

$ ruby -e 'mb = 380; a="z"*mb*2**20; puts "=)"' 
=) 


poi fare la stringa leggermente più grande:

$ ruby -e 'mb = 385; a="z"*mb*2**20; puts "=)"' 
-e:1:in `*': failed to allocate memory (NoMemoryError) 
     from -e:1:in `<main>' 


Aggiungi un fork al processo di rubino, riducendo mb fino a quando non viene eseguito.

$ ruby -e 'mb = 195; a="z"*mb*2**20; fork; puts "=)"' 
=) 


Un leggermente più grande processo forcella produrrà l'errore ENOMEM:

$ ruby -e 'mb = 200; a="z"*mb*2**20; fork; puts "=)"' 
-e:1:in `fork': Cannot allocate memory - fork(2) (Errno::ENOMEM) 
     from -e:1:in `<main>' 


esecuzione di un comando con apici inversi lancia tale processo con un fork modo ha lo stesso risultato:

$ ruby -e 'mb = 200; a="z"*mb*2**20; `ls`' 
-e:1:in ``': Cannot allocate memory - ls (Errno::ENOMEM) 
     from -e:1:in `<main>' 


Così ci sei, hai bisogno di circa il doppio dei processi genitore di memoria disponibile in grado sul sistema di imporre un nuovo processo. MRI Ruby fa molto affidamento su fork per il suo modello multiprocesso, questo è dovuto alla progettazione di Ruby che utilizza uno global interpreter lock (GIL) che consente l'esecuzione di un solo thread alla volta per processo di ruby.

Credo che Python abbia un uso molto inferiore di fork internamente. Quando si utilizza os.fork in Python, lo stesso si verifica se:

python -c 'a="c"*420*2**20;' 
python -c 'import os; a="c"*200*2**20; os.fork()' 


Oracle hanno un detailed article on the problem e parlare con l'alternativa di posix_spawn(). L'articolo è diretto a Solaris, ma questo è un problema generale di POSIX Unix, quindi si applica a Linux (se non alla maggior parte degli Unix).

Esiste anche un'implementazione Ruby di posix-spawn che è possibile utilizzare se si ha il controllo del codice. Questo modulo non sostituisce nulla in Rails, quindi non sarà di aiuto qui a meno che tu non abbia sostituito personalmente le chiamate allo fork.

+0

un giorno improvvisamente abbiamo avuto un problema simile 'Errno :: ENOMEM: Impossibile allocare memoria - file -b --mime/tmp/blabla.jpg' e questo è dovuto al fatto che abbiamo introdotto un ulteriore processo di monitoraggio (agente DataDog che consumava 600 MB) ... comunque '+ 1' sul controllo' free -m' :) – equivalent8

+0

Wow, è ridicolmente grande per un agente di monitoraggio. – Matt

Problemi correlati