Table of contents
Introduction
- What is Ruby Enterprise Edition?
- 33% memory reduction and faster? Is this for real?
- Is it stable?
- I already have Ruby installed. Will Ruby Enterprise Edition conflict with it?
- Is it compatible with all my existing Ruby apps? Can I run Rails on Ruby Enterprise Edition?
- Why did you fork Ruby?
- Does this work with Mongrel?
Users and developers
"What is Ruby Enterprise Edition?"
Ruby Enterprise Edition is a branch of Ruby, which includes:
- An enhanced garbage collector. This allows one to reduce memory usage of Ruby on Rails applications (or any Ruby application that takes advantage of the feature) by 33% on average.
- An improved memory allocator. This increases Ruby's performance drastically.
- Various developer tools for debugging memory usage and garbage collector behavior.
If you're interested in the technical details behind Ruby Enterprise Edition, here's a short summary of how it works:
It is observed that a lot of memory in Ruby on Rails applications is taken by AST nodes (of both the Rails framework and the application source). People generally run multiple Rails application instances (application servers) to serve a Rails website. If the different Rails application instances can share the memory of the AST nodes with each other, much like how C/C++ programs/libraries share program code with each other, then it will result in significant reduction of memory usage.
The POSIX API allows one to implement this, by using
the fork()
system call. On modern Unix implementations
(Linux, FreeBSD, MacOS X, whatever), a child process created by
fork()
shares most of its memory with its parent process.
If one forks a 500 MB process, it will not result in 1 GB memory
usage, but probably like 500.1 MB. When the child process (or
the parent process) writes to a piece of memory, then that memory
is copied, preventing the parent and child process from writing to
each others' memory. This process is called "copy-on-write".
However, Ruby's garbage collector is not copy-on-write friendly. Its mark-and-sweep algorithm implementation stores the mark bit directly inside objects. A garbage collection cycle will thus result in all objects being written to (or in operating systems jargon: the memory pages of the objects are made dirty). The OS will copy all that memory, thus negating the effect of copy-on-write.
We at Phusion have researched possibilities to make Ruby's garbage collector copy-on-write friendly - and eventually, we succeeded. Ruby Enterprise Edition is a polished product based on this research, and is optimized for ease of use for end users (= system administrators who run Ruby/Rails applications). Readers who are interested in our research process should read the "Making Ruby's garbage collector copy-on-write friendly" blog series (ordered from oldest to newest):
- Saving memory in Ruby on Rails with fork() and copy-on-write
- Potential problems with preforking Ruby on Rails
- Saving memory in Ruby on Rails with fork() - failed (this is where we thought that we'll give up, but eventually didn't)
- Making Ruby's garbage collector copy-on-write friendly
- Making Ruby's garbage collector copy-on-write friendly, part 2
- Making Ruby's garbage collector copy-on-write friendly, part 3
- Making Ruby's garbage collector copy-on-write friendly, part 4
- Making Ruby's garbage collector copy-on-write friendly, part 5
- Making Ruby's garbage collector copy-on-write friendly, part 6 (final?)
- Making Ruby's garbage collector copy-on-write friendly, part 7
"33% memory reduction and faster? Is this for real?"
- "33%" is just an average estimate. Your milage may vary, depending on the specific application and the workload.
-
It is no silver bullet. Software needs to be written/adapted to take advantage of the reduced memory usage offered by Ruby Enterprise Edition. Phusion Passenger (aka mod_rails), our easy-to-use Ruby on Rails deployment solution for the Apache web server, automatically makes use of Ruby Enterprise Edition's features.
"Is it stable?"
"I already have Ruby installed. Will Ruby Enterprise Edition conflict with it?"
It will not. By default, Ruby Enterprise Edition is installed into a self-contained directory. None of your system files, or files owned by the existing Ruby, will be touched. Uninstalling Ruby Enterprise Edition is as easy as removing that directory.
Installing Ruby Enterprise Edition is 100% safe. You do not even need root privileges to install Ruby Enterprise Edition!
"Is it compatible with all my existing Ruby apps? Can I run Rails on Ruby Enterprise Edition?"
Yes it is. And yes you can. Ruby Enterprise Edition is 100% compatible with Ruby 1.8.6. Ruby applications do not require any modifications to run on Ruby Enterprise Edition.
"Why did you fork Ruby?"
We're actively working with the upstream Ruby developers to get as many improvements as possible merged back. That said, merging improvements back to upstream is a slow process. There exists demand for lower memory usage in Rails application, today. Therefore we have taken the initiative to maintain our own Ruby branch.
We use the Git version control system to support this non-linear style of development. Git makes it relatively easy keep our codebase up to date with that of upstream Ruby's, and vice-versa. Please read the development page for details.
"Does this work with Mongrel?"
Mongrel runs fine on Ruby Enterprise Edition. But if you meant whether Mongrel will use less memory, then the answer is no (at the time of writing). Mongrel must be adapted to make use of the abilities provided by Ruby Enterprise Edition. We recommend you to use Phusion Passenger, which makes full use of Ruby Enterprise Edition's abilities to save as much memory as possible.
"When I run an app in Ruby Enterprise Edition, it can't find certain libraries, but I know they're installed!"
Ruby Enterprise Edition does not look in regular Ruby's library search paths. So any third party libraries will have to be reinstalled for Ruby Enterprise Edition.
The library in question is probably a gem. Please use Ruby Enterprise Edition's RubyGems (instead of the regular Ruby's RubyGems) to install it. For example:
/opt/ruby-enterprise-X.X.X/bin/gem install some_gem_name(assuming that you installed Ruby Enterprise Edition to "/opt/ruby-enterprise-X.X.X")
"How do I uninstall Ruby Enterprise Edition?"
"How can I adapt my applications to take advantage of Ruby Enterprise Edition's copy-on-write friendly garbage collector?"
GC.copy_on_write_friendly
to true:
if GC.respond_to?(:copy_on_write_friendly=) GC.copy_on_write_friendly = true endYou should do this before you call
fork
.
As you can see, the above code will work fine even when running in regular Ruby.
How can I debug the memory usage or garbage collector behavior?
puts ObjectSpace.statisticsIt will output something along the lines of:
Number of objects : 27488 (20482 AST nodes, 74.51%) Heap slot size : 20 GC cycles so far : 1 Number of heaps : 2 Total size of objects: 536.88 KB Total size of heaps : 546.89 KB (10.02 KB = 1.83% overhead) Leading free slots : 0 (0.00 KB = 0.00%) Trailing free slots : 0 (0.00 KB = 0.00%) Number of contiguous groups of 16 slots: 1 (0.06%) Number of terminal objects: 464 (1.66%)The following fields are particularily interesting to application developers:
- Number of objects
- Number of currently allocated Ruby objects.
- GC cycles so far
- The number of times so far that the garbage collector has been invoked.
- Number of heaps
-
The number of Ruby heaps currently allocated. A Ruby heap (not to be confused with the system heap) is a piece of fixed-size memory which contains a collection of Ruby objects.
A heap consists of free slots (in which new Ruby objects can be allocated) and used slots (in which Ruby objects are already allocated).
The initial heap has 10000 slots. Each successive heap is larger than the previous one, by 1.8 times.
- Total size of objects
-
The total size of all objects in the Ruby heap. This is equal to:
"heap slot size" * "number of objects"
Note that this number doesn't include the size of any additional data associated with the objects. So suppose you only have 3 String objects on the heap, each one associated with 100 MB string data. Then "Total size of objects" will be3 * 20 = 60 bytes
, not "300 MB + 60 bytes". - Total size of heaps
-
The total size of all heaps. That is, the memory of used slots plus the memory of free slots.
"overhead" indicates the amount of memory that is dedicated to free slots.
The remaining information are purely for our own investigation/debugging purposes.