Ruby: Cannot allocate memory
When Ruby calls fork
the OS will make a copy of the entire parent processes address space, even if fork is only being called to exec
another small process like ls
. Momentarily, your system needs to be able to allocate a chunk of memory at least the size of the Ruby parent process before collapsing it down to what the child process actually needs.
So rails is generally quite memory hungry. Then if something uses fork
, you need twice as much memory.
TL;DR Use posix-spawn instead of fork if you are in control of the code. Otherwise give your VM 1024MB or a bit of extra swap space to take up the slack for the fork
call
Example Ruby Memory Usage withfork
Take a random VM, this one has swap space disabled:
$ free -m
total used free shared buffers cached
Mem: 1009 571 438 0 1 35
-/+ buffers/cache: 534 475
Swap: 0 0 0
Look at the Mem:
row and free
column. This is around about your size limit for a new process, in my case 438
MiB
My buffers/cached
have already been flushed for this test so that my free
memory is at it's limit. You may need to take the buffers/cache
values into account if they are large. Linux has the ability to evict stale cache when memory is needed by a process.
Use up some memory
Create a ruby process with a string around the size of your free memory. There is some overhead for the ruby
process so it's not going to exactly match free
.
$ ruby -e 'mb = 380; a="z"*mb*2**20; puts "=)"'
=)
Then make the string slightly larger:
$ ruby -e 'mb = 385; a="z"*mb*2**20; puts "=)"'
-e:1:in `*': failed to allocate memory (NoMemoryError)
from -e:1:in `<main>'
Add a fork
to the ruby process, reducing mb
until it runs.
$ ruby -e 'mb = 195; a="z"*mb*2**20; fork; puts "=)"'
=)
A slightly larger fork process will produce the ENOMEM
error:
$ 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>'
Running a command with backticks launches that process with a fork
so has the same outcome:
$ ruby -e 'mb = 200; a="z"*mb*2**20; `ls`'
-e:1:in ``': Cannot allocate memory - ls (Errno::ENOMEM)
from -e:1:in `<main>'
So there you go, you need about twice the parent processes memory available on the system to fork a new process. MRI Ruby relies heavily on fork
for it's multi process model, this is due to the design of Ruby which uses a global interpreter lock (GIL) that only allows one thread to execute at a time per ruby process.
I believe Python has a lot less use of fork
internally. When you do use os.fork
in Python, the same occurs though:
python -c 'a="c"*420*2**20;'
python -c 'import os; a="c"*200*2**20; os.fork()'
Oracle have a detailed article on the problem and talk about using the alternative of posix_spawn()
. The article is directed at Solaris but this is a general POSIX Unix issue so applies to Linux (if not most Unices).
There is also a Ruby implementation of posix-spawn
which you could use if you are in control of the code. This module doesn't replace anything in Rails, so it won't help you here unless you replaced the calls to fork
yourself.
Getting error Cannot allocate memory for Rails
Both IO.popen
and Kernel#system
can be expensive operations in terms of memory because they both rely on fork(2). Fork(2) is a Unix system call which creates a child process that clones the parent's memory and resources. That means, if your parent process uses 500mb of memory, then your child would also use 500mb of memory. Each time you do Kernel#system
or IO.popen
you increase your application's memory usage by the amount of memory it takes to run your Rails app.
If your development machine has more RAM than your production server or if your production server produces a lot more output, there are two things you could do:
- Increase memory for your production server.
- Do some memory management using something like Resque.
You can use Resque to queue those operations as jobs. Resque will then spawn "workers"/child processes to get a job from the queue, work on it and then exit. Resque still forks, but the important thing is that the worker exits after working on the task so that frees up memory. There'll be a spike in memory every time a worker does a job, but it will go back to the baseline memory of your app every after it.
You might have to do both options above and look for other ways to minimize the memory-usage of your app.
Errno::ENOMEM: Cannot allocate memory - cat
So it seems that your system is running pretty low on memory and spawning a shell + calling cat is too much for the few memory left.
If you don't mind loosing some speed, you can merge the files in ruby, with small buffers.
This avoids spawning a shell, and you can control the buffer size.
This is untested but you get the idea :
buffer_size = 4096
output_file = File.open(final_output_file, 'w')
Dir["#{processing_directory}/*.csv"].sort_by {|file| [file.count("/"), file]}.each do |file|
f = File.open(file)
while buffer = f.read(buffer_size)
output_file.write(buffer)
end
f.close
end
Cannot allocate memory for activeadmin in ubuntu 14.04 with DigitalOcean
I have found solution that I do need some more memory for bundle install. That's why I have created swap in harddisk with 512MB memory and it is working fine for me.
You can create swap from here.
Rails app Cannot allocate memory when saving photo using Paperclip
Have you checked your current memory usage on your server? This issue often occurs when there are only a little of memory left. As you said, this does not happen all the time so I think lack of memory is the main reason.
If you can not add more memory right away to your server, try adding a swap partition on your server can also be helpful.
Related Topics
How to Click on a Specific Coordinates of an Element
How to Convert a Ruby String with Brackets to an Array
Ruby on Rails 4 Select Multiple
Authlogic and Multiple Sessions for the Same User
Ruby: Method Inexplicably Overwritten and Set to Nil
How to Execute Windows Cli Commands in Ruby
How to Find Gems That Depend on a Given Gem
How to Store an Instance Variable Across Multiple Actions in a Controller
Slicing Params Hash for Specific Values
In Ruby How to Use Class Level Local Variable? (A Ruby Newbie's Question)
Tcp Socket Communication Between Processes on Heroku Worker Dyno
How to Use Functions Like Concat(), etc. in Arel
Ruby Indented Multiline Strings
Using Bsearch to Find Index for Inserting New Element into Sorted Array
How to Remove Non-Printable/Invisible Characters in Ruby
Heroku Rails 3.1 App - Compiling Assets Locally VS Compiling Assets During Slug Compilation