Kiedy Ruby zadzwoni pod numer fork
, system operacyjny wykona kopię całej nadrzędnej przestrzeni adresowej procesów, nawet jeśli widełki będą wywoływane tylko pod exec
inny mały proces, taki jak ls
. Chwile, twój system musi mieć możliwość przydzielenia kawałka pamięci co najmniej o rozmiar przedinty rodzica Rubiego, zanim zwinnie go do tego, czego proces potomny potrzebuje.
Tak więc szyny są generalnie dość głodne pamięci. Następnie, jeśli coś używa fork
, potrzebujesz dwa razy więcej pamięci.
TL; DR Użyj posix-spawn zamiast widelca, jeśli masz kontrolę nad kodem. W przeciwnym razie dać VM 1024MB lub trochę więcej przestrzeni wymiany, aby wypełnić powstałą lukę na fork
rozmowy
Usage
Przykład Ruby Memory z fork
Take losową VM, ten ma przestrzeń wymiany niepełnosprawnych:
$ free -m
total used free shared buffers cached
Mem: 1009 571 438 0 1 35
-/+ buffers/cache: 534 475
Swap: 0 0 0
spojrzenie na Mem:
free
rzędu i kolumn. Jest to o około limit rozmiaru dla nowego procesu, w moim przypadku 438
MiB
My buffers/cached
zostały już flushed dla tego testu tak, że moja pamięć free
jest to ograniczenie. Może być konieczne uwzględnienie wartości buffers/cache
, jeśli są one duże. Linux ma możliwość eksmisji nieaktualnych pamięci podręcznych, gdy pamięć jest potrzebna w procesie.
zużyć trochę pamięci
Utwórz proces rubinowy sznurkiem wokół wielkości wolnej pamięci. Jest pewien narzut dla procesu ruby
, więc nie będzie dokładnie pasował do free
.
$ ruby -e 'mb = 380; a="z"*mb*2**20; puts "=)"'
=)
Następnie zrób ciąg nieco większy:
$ ruby -e 'mb = 385; a="z"*mb*2**20; puts "=)"'
-e:1:in `*': failed to allocate memory (NoMemoryError)
from -e:1:in `<main>'
Dodaj fork
do procesu ruby, zmniejszając mb
aż skończy.
$ ruby -e 'mb = 195; a="z"*mb*2**20; fork; puts "=)"'
=)
Nieco większy proces widelec będzie produkować błąd 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>'
Uruchomienie polecenia z backticks uruchamia ten proces z fork
więc ten sam wynik:
$ ruby -e 'mb = 200; a="z"*mb*2**20; `ls`'
-e:1:in ``': Cannot allocate memory - ls (Errno::ENOMEM)
from -e:1:in `<main>'
Tak więc, potrzebujesz około dwa razy więcej niż pamięć nadrzędna w stanie w systemie rozwidlić nowy proces. MRI Ruby opiera się w dużej mierze na fork
, ponieważ jest to model wieloprocesowy, jest to spowodowane projektem Ruby, który używa global interpreter lock (GIL), który pozwala tylko na wykonanie jednego wątku w czasie na proces ruby.
Wierzę, że Python ma wewnętrznie mniejszą przydatność do używania fork
. Kiedy używasz os.fork
w Pythonie, to samo dzieje się jednak:
python -c 'a="c"*420*2**20;'
python -c 'import os; a="c"*200*2**20; os.fork()'
Oracle mają detailed article on the problem i rozmawiać na temat korzystania z alternatywy posix_spawn()
. Artykuł jest skierowany do Solaris, ale jest to ogólny problem z Uniksem w systemie POSIX, dotyczy to więc Linuksa (jeśli nie większości Uniksów).
Istnieje również implementacja języka Ruby w wersji posix-spawn
, której można użyć, jeśli masz kontrolę nad kodem. Ten moduł nie zastępuje niczego w Railsach, więc nie pomoże ci tutaj, chyba że sam zmienisz połączenia na fork
.
Trudno powiedzieć, czy potrzebujesz więcej pamięci RAM. Ogólnie rzecz biorąc, średnia aplikacja szyn może korzystać z 512M. Railsy ładują całą aplikację do pamięci x jakkolwiek wiele serwerów/procesów, które masz zamiar. Zwykle ustawiam pamięć RAM na 2 GB, aby nawet próbować. Robię moje dev na moim Macu, chociaż ogólnie, nie Virtual Box. –