Ruby:无法分配内存

我正在开发Ruby on Rails应用程序。 我是Ruby / Rails的新手。 我使用Ruby 2.2.0和Rails 4.2。 当我运行如下命令时:

rails g migration SomeMigrationName 

它失败了

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

我在2014年中期使用Macbook Pro和OS X 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超出限制时会出现这样的错误,但我使用相同的配置(Vagrant文​​件)用于运行多个Python / Tornado应用程序,MongoDB和Redis的另一个开发环境,它一切正常。

我是否需要增加vb.memory值或者它是一个Ruby错误?

当Ruby调用fork ,操作系统将复制整个父进程的地址空间,即使只调用fork来exec另一个像ls这样的小进程。 暂时,您的系统需要能够分配一块至少与Ruby父进程大小相同的内存,然后再将其折叠到子进程实际需要的内容。

因此,铁轨通常非常耗费内存。 然后如果有东西使用fork ,你需要两倍的内存。

TL; DR如果您控制代码,请使用posix-spawn而不是fork。 否则给你的VM 1024MB或一些额外的交换空间来消除fork调用的松弛

使用fork Ruby内存使用示例

拿一个随机虚拟机,这个虚拟机已禁用交换空间:

 $ 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值很大,则可能需要考虑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 `
'

在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 `
'

使用反引号运行命令会使用fork启动该进程,因此具有相同的结果:

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

所以,你需要大约两倍于系统上可用的父进程内存来分叉一个新进程。 MRI Ruby依赖于fork的多进程模型,这是由于Ruby的设计使用了全局解释器锁(GIL) ,它只允许每个ruby进程一次执行一个线程。

我相信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)。

还有一个posix-spawn的Ruby实现,如果你控制代码,你可以使用它。 此模块不会替换Rails中的任何内容,因此除非您自己将fork替换为fork否则它不会对您有所帮助。