私はRuby on Railsアプリケーションの開発中です。私はRuby/Railsの初心者です。Ruby 2.2.0およびRails 4.2。次のようなコマンドを実行すると、次のようになります。
Rails g migration SomeMigrationName
それは失敗します
Cannot allocate memory - fork(2) (Errno::ENOMEM)
MacbookPro2014年半ばにOSX 10.10を搭載し、Vagrant/Virtualboxを使用して、Rails開発用の仮想マシン(Ubuntu 14.04)を実行しています。
これが私の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
RAMが制限を超えている場合にこのようなエラーが発生することを読みましたが、いくつかのPython/Tornadoアプリ、MongoDB、Redisを実行する別の開発環境に同じ構成(Vagrantファイル)を使用していますそしてそれはすべてうまくいきます。
Vb.memory値を増やす必要がありますか、それともRubyバグですか?
Rubyがfork
を呼び出すと、forkがexec
にのみ呼び出されている場合でも、OSは親プロセスのアドレス空間全体のコピーを作成します。 ls
。一時的に、システムは、子プロセスが実際に必要とするものに折りたたむ前に、少なくともRuby親プロセスのサイズのメモリのチャンクを割り当てることができる必要があります。
したがって、Railsは通常、かなりメモリを消費します。次に、何かがfork
を使用する場合、2倍のメモリが必要になります。
TL; DRコードを制御している場合は、フォークの代わりに posix-spawn を使用します。それ以外の場合は、VM 1024MBまたは少し余分なスワップスペースを与えて、fork
呼び出しの余裕を取ります
例Rubyメモリ使用量withfork
ランダムなVMを使用します。これは、スワップスペースが無効になっています。
_$ free -m
total used free shared buffers cached
Mem: 1009 571 438 0 1 35
-/+ buffers/cache: 534 475
Swap: 0 0 0
_
_Mem:
_行とfree
列を見てください。これは、新しいプロセスのサイズ制限とほぼ同じです。私の場合は_438
_ MiB
私の_buffers/cached
_はすでにこのテストで フラッシュ されているので、私のfree
メモリは限界に達しています。値が大きい場合は、_buffers/cache
_値を考慮する必要がある場合があります。 Linuxには、プロセスでメモリが必要になったときに古いキャッシュを削除する機能があります。
メモリを使い果たします
空きメモリのサイズ程度の文字列を使用してRubyプロセスを作成します。Ruby
プロセスにはオーバーヘッドがあるため、free
と完全には一致しません。 。
_$ Ruby -e 'mb = 380; a="z"*mb*2**20; puts "=)"'
=)
_
次に、文字列を少し大きくします。
_$ Ruby -e 'mb = 385; a="z"*mb*2**20; puts "=)"'
-e:1:in `*': failed to allocate memory (NoMemoryError)
from -e:1:in `<main>'
_
Rubyプロセスにfork
を追加し、実行されるまでmb
を減らします。
_$ Ruby -e 'mb = 195; a="z"*mb*2**20; fork; puts "=)"'
=)
_
少し大きいforkプロセスでは、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>'
_
バッククォートを使用してコマンドを実行すると、そのプロセスがfork
で起動するため、同じ結果になります。
_$ Ruby -e 'mb = 200; a="z"*mb*2**20; `ls`'
-e:1:in ``': Cannot allocate memory - ls (Errno::ENOMEM)
from -e:1:in `<main>'
_
これで、新しいプロセスをフォークするには、システムで使用可能な親プロセスのメモリの約2倍が必要になります。 MRI Rubyマルチプロセスモデルはfork
に大きく依存しています。これは、 を使用するRubyの設計によるものです。グローバルインタプリタロック(GIL) Rubyプロセスごとに、一度に1つのスレッドのみを実行できるようにします。
Pythonは内部でfork
の使用がはるかに少ないと思います。Pythonで_os.fork
_を使用すると、同じことが起こります。
_python -c 'a="c"*420*2**20;'
python -c 'import os; a="c"*200*2**20; os.fork()'
_
Oracleには 問題の詳細な記事 があり、posix_spawn()
の代替の使用について話し合っています。この記事はSolarisを対象としていますが、これは一般的なPOSIX Unixの問題であるため、Linuxに適用されます(ほとんどのUnicesではない場合)。
Ruby _posix-spawn
_ の実装もあります。これは、コードを制御している場合に使用できます。このモジュールは、Railsの何も置き換えません。 、したがって、fork
への呼び出しを自分で置き換えない限り、ここでは役に立ちません。