Ruby: 无法分配内存
Ruby: Cannot allocate memory
我正在 Rails 应用程序上开发 Ruby。我是 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 超出限制时会发生这样的错误,但我对另一个 运行 几个 Python/Tornado 应用程序的开发环境使用相同的配置(Vagrant 文件), MongoDB 和 Redis,一切正常。
我需要增加 vb.memory 值还是 Ruby 错误?
当 Ruby 调用 fork
时 OS 将复制整个父进程地址 space,即使 fork 仅被调用到 [=18] =] 另一个像 ls
这样的小过程。暂时,您的系统需要能够分配至少与 Ruby 父进程大小相当的内存块,然后才能将其折叠到子进程实际需要的大小。
所以 rails 通常非常耗费内存。那么如果某些东西使用 fork
,你需要两倍的内存。
TL;DR 如果您可以控制代码,请使用 posix-spawn 而不是 fork。否则给你的 VM 1024MB 或一些额外的交换空间 space 来弥补 fork
调用
的空闲时间
示例 Ruby 内存使用情况 fork
使用一个随机的 VM,这个已经禁用交换 space:
$ 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
已经 flushed 参加了这次测试,所以我的 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>'
所以你开始了,你需要大约两倍于系统上可用的父进程内存来派生一个新进程。 MRI Ruby 的多进程模型在很大程度上依赖于 fork
,这是由于 Ruby 的设计使用了一次只允许一个线程执行的 global interpreter lock (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 有一个 detailed article on the problem 并讨论使用 posix_spawn()
的替代方法。这篇文章针对 Solaris,但这是一个普遍的 POSIX Unix 问题,因此适用于 Linux(如果不是大多数 Unix)。
还有 posix-spawn
的 Ruby 实现,如果您可以控制代码,则可以使用它。此模块不会替换 Rails 中的任何内容,因此除非您自己替换对 fork
的调用,否则它不会在这里为您提供帮助。
我正在 Rails 应用程序上开发 Ruby。我是 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 超出限制时会发生这样的错误,但我对另一个 运行 几个 Python/Tornado 应用程序的开发环境使用相同的配置(Vagrant 文件), MongoDB 和 Redis,一切正常。
我需要增加 vb.memory 值还是 Ruby 错误?
当 Ruby 调用 fork
时 OS 将复制整个父进程地址 space,即使 fork 仅被调用到 [=18] =] 另一个像 ls
这样的小过程。暂时,您的系统需要能够分配至少与 Ruby 父进程大小相当的内存块,然后才能将其折叠到子进程实际需要的大小。
所以 rails 通常非常耗费内存。那么如果某些东西使用 fork
,你需要两倍的内存。
TL;DR 如果您可以控制代码,请使用 posix-spawn 而不是 fork。否则给你的 VM 1024MB 或一些额外的交换空间 space 来弥补 fork
调用
示例 Ruby 内存使用情况 fork
使用一个随机的 VM,这个已经禁用交换 space:
$ 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
已经 flushed 参加了这次测试,所以我的 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>'
所以你开始了,你需要大约两倍于系统上可用的父进程内存来派生一个新进程。 MRI Ruby 的多进程模型在很大程度上依赖于 fork
,这是由于 Ruby 的设计使用了一次只允许一个线程执行的 global interpreter lock (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 有一个 detailed article on the problem 并讨论使用 posix_spawn()
的替代方法。这篇文章针对 Solaris,但这是一个普遍的 POSIX Unix 问题,因此适用于 Linux(如果不是大多数 Unix)。
还有 posix-spawn
的 Ruby 实现,如果您可以控制代码,则可以使用它。此模块不会替换 Rails 中的任何内容,因此除非您自己替换对 fork
的调用,否则它不会在这里为您提供帮助。