web-dev-qa-db-ja.com

Ruby:メモリを割り当てることができません

私は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バグですか?

11

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への呼び出しを自分で置き換えない限り、ここでは役に立ちません。

27
Matt