2015-02-02 13 views
10

Ich bin im Prozess der Entwicklung einer Ruby on Rails-Anwendung. Ich bin ein Neuling für Ruby/Rails. Ich benutze Ruby 2.2.0 und Rails 4.2. Wenn ich einen Befehl wie ausführen:Ruby: Speicher kann nicht zugeordnet werden

rails g migration SomeMigrationName 

schlägt es mit dem

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

verwende ich Pro Mitte Macbook 2014 mit OS X 10.10 an Bord und Vagrant/Virtualbox eine virtuelle Maschine (Ubuntu 14.04) laufen für die Entwicklung von Schienen.

Hier ist meine Vagrant-Datei:

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 

Ich habe gelesen, dass ein solcher Fehler tritt auf, wenn RAM aus Grenze, aber ich benutze gleiche config (Vagrant-Datei) für die anderen Entwickler-Umgebung, die mehrere läuft Python/Tornado Apps, MongoDB und Redis und alles funktioniert gut.

Muss ich den vb.memory Wert erhöhen oder es ist ein Ruby Bug?

+0

Es ist schwer zu sagen, ob Sie mehr RAM benötigen. Im Allgemeinen kann eine mittelgroße Rails-App 512M verwenden. Rails lädt die gesamte Anwendung in den Speicher x, aber viele Server/Prozesse, die Sie verwenden. Ich setze RAM generell auf 2GB, um es überhaupt zu versuchen. Ich mache meinen Entwickler im Allgemeinen, aber keine virtuelle Box. –

Antwort

23

Wenn Ruby fork aufruft, erstellt das Betriebssystem eine Kopie des Adressraums der gesamten übergeordneten Prozesse, selbst wenn fork nur exec aufgerufen wird, ein anderer kleiner Prozess wie ls. Momentan muss Ihr System in der Lage sein, einen Teil des Speichers mit mindestens der Größe der Ruby-Parent-Orizisse zuzuweisen, bevor es auf den tatsächlichen Wert des untergeordneten Prozesses reduziert wird.

So Schienen ist im Allgemeinen ziemlich Speicher hungrig. Dann, wenn etwas fork verwendet, benötigen Sie doppelt so viel Speicher.

TL; DR Verwenden Sie posix-spawn statt Gabel, wenn Sie die Kontrolle über den Code haben. Andernfalls wird Ihr VM 1024MB oder ein bisschen mehr Swap-Speicher geben, um die Flaute für die fork Anruf zu nehmen


Beispiel Ruby-Speichernutzung mit fork

Nehmen Sie eine zufällige VM, hat dieser ein Swap-Speicher deaktiviert ist:

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

Blick auf die Mem: Zeile und Spalte free. Dies ist um über Ihre Größenbeschränkung für einen neuen Prozess, in meinem Fall 438 MiB

Mein buffers/cached wurden bereits flushed für diesen Test, so dass mein free Speicher ist es Grenze. Möglicherweise müssen Sie die Werte buffers/cache berücksichtigen, wenn sie groß sind. Linux hat die Fähigkeit, veralteten Cache zu verwerfen, wenn Speicher von einem Prozess benötigt wird.


Verwenden Sie einige Speicher

erstellen Ruby-Prozess mit einer Schnur um die Größe des freien Speichers auf. Es gibt einige Overhead für den ruby Prozess, so dass es nicht genau free entsprechen wird.

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


Dann wird die Saite machen etwas größer:

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


eine fork zum Ruby-Prozess hinzufügen, reduziert mb, bis es läuft.

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


Ein etwas größeres Gabel Prozess wird die ENOMEM Fehler erzeugen:

$ 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>' 


einen Befehl mit Backticks Rennen startet das Verfahren mit einem fork so das gleiche Ergebnis hat:

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


Also los geht's, du brauchst etwa doppelt so viel wie der Elternprozessspeicher in der Lage, einen neuen Prozess zu starten. MRI Ruby basiert stark auf fork für sein Multi-Prozess-Modell, dies liegt am Design von Ruby, das eine global interpreter lock (GIL) verwendet, die nur einen Thread pro Rubin-Prozess gleichzeitig ausführen lässt.

Ich glaube, Python hat viel weniger Verwendung von fork intern. Wenn Sie os.fork in Python zu tun verwenden, tritt das gleiche aber:

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


Oracle eine detailed article on the problem haben und über die Verwendung der Alternative von posix_spawn() zu sprechen. Der Artikel richtet sich an Solaris, aber dies ist ein allgemeines POSIX-Unix-Problem, das gilt für Linux (wenn nicht die meisten Unices).

Es gibt auch eine Ruby-Implementierung von posix-spawn, die Sie verwenden könnten, wenn Sie die Kontrolle über den Code haben. Dieses Modul ersetzt nichts in Rails, daher wird es Ihnen hier nicht weiterhelfen, wenn Sie die Aufrufe von fork nicht selbst ersetzen.

+0

Eines Tages hatten wir plötzlich ein ähnliches Problem 'Errno :: ENOMEM: Speicher kann nicht zugewiesen werden - Datei -b --mime/tmp/blabla.jpg' und das war aufgrund der Tatsache, dass wir zusätzliche Überwachung Prozess eingeführt (DataDog-Agent, der 600MB verbraucht) ... sowieso '+ 1' auf dem' free -m' check :) – equivalent8

+0

Wow, das ist lächerlich groß für einen Überwachungsagenten. – Matt

Verwandte Themen