views:

712

answers:

11

Ruby is truly memory-hungry - but also worth every single bit.

What do you do to keep the memory usage low? Do you avoid big strings and use smaller arrays/hashes instead or is it no problem to concern about for you and let the garbage collector do the job?

Edit: I found a nice article about this topic here and another one here - old but still interesting.

+3  A: 
alexmac
In Ruby it's a bit worse, because the garbage collection can take a lot of time and you can't destroy a variable directly.
unexist
bad advice. Keep variables in scope longer, but strings are the killer for memory - appending does too much memory reallocs, so appending may even be your problem in the first place.
gbjbaanb
Yes but sometimes it is unavoidable to have to append a string etc. It is better to do any work on a smaller string then add it on the larger. In .net I would use a string builder for this. He wanted to reduce memory usage and if you were not using a variable why keep it around?
alexmac
+1  A: 

I'm pretty new at Ruby, but so far I haven't found it necessary to do anything special in this regard (that is, beyond what I just tend to do as a programmer generally). Maybe this is because memory is cheaper than the time it would take to seriously optimize for it (my Ruby code runs on machines with 4-12 GB of RAM). It might also be because the jobs I'm using it for are not long-running (i.e. it's going to depend on your application).

John Zwinck
+6  A: 

I've found Phusion's Ruby Enterprise Edition (a fork of mainline Ruby with much-improved garbage collection) to make a dramatic difference in memory usage... Plus, they've made it extraordinarily easy to install (and to remove, if you find the need).

You can find out more and download it on their website.

Ben Scofield
+2  A: 

Other than in extreme cases memory usage isn't something to worry about. The time you spend trying to reduce memory usage will buy a LOT of gigabytes.

Loren Pechtel
+4  A: 

I really don't think it matters all that much. Making your code less readable in order to improve memory consumption is something you should only ever do if you need it. And by need, I mean have a specific case for the performance profile and specific metrics that indicate that any change will address the issue.

If you have an application where memory is going to be the limiting factor, then Ruby may not be the best choice. That said, I have found that my Rails apps generally consume about 40-60mb of RAM per Mongrel instance. In the scheme of things, this isn't very much.

You might be able to run your application on the JVM with JRuby - the Ruby VM is currently not as advanced as the JVM for memory management and garbage collection. The 1.9 release is adding many improvements and there are alternative VM's under development as well.

Toby Hede
The problem is not that a mongrel instance needs 60M - it's more that I have to restart mongrel every day to avoid huge leakage - but that's not what I meant with this question. ;)
unexist
Very true. I think one of the BIG gains from Passenger is in terms of the management of the instances. I haven't moved my server over yet, but I am looking to do it soon.
Toby Hede
I actually prefer Thin over Mongrel at the moment - but the problem is Ruby internal. Does the 1.9 include any fixes for the garbage collector?
unexist
A: 

I try to keep arrays & lists & datasets as small as possible. The individual object do not matter much, as creation and garbage collection is pretty fast in most modern languages.

In the cases you have to read some sort of huge dataset from the database, make sure to read in a forward/only manner and process it in little bits instead og loading everything into memory first.

Sebastian
+1  A: 
  1. Choose date structures that are efficient representations, scale well, and do what you need.
  2. Use algorithms that work using efficient data structures rather than bloated, but easier ones.
  3. Look else where. Ruby has a C bridge and its much easier to be memory conscious in C than in Ruby.
The C-bridge isn't well documented and you'll always have to look yourself. Most google links are outdated docs/references to non-existing pages. ;)
unexist
+2  A: 

Take a look at Small Memory Software - Patterns for Systems with Limited Memory. You don't specify what sort of memory constraint, but I assume RAM. While not Ruby-specific, I think you'll find some useful ideas in this book - the patterns cover RAM, ROM and secondary storage, and are divided into major techniques of small data structures, memory allocation, compression, secondary storage, and small architecture.

Kevin Haines
+2  A: 

The only thing we've ever had which has actually been worth worrying about is RMagick.

The solution is to make sure you're using RMagick version 2, and call Image#destroy! when you're done using your image

Orion Edwards
+1  A: 

Avoid code like this:

str = ''
veryLargeArray.each do |foo|
  str += foo
  # but str << foo is fine (read update below)
end

which will create each intermediate string value as a String object and then remove its only reference on the next iteration. This junks up the memory with tons of increasingly long strings that have to be garbage collected.

Instead, use Array#join:

str = veryLargeArray.join('')

This is implemented in C very efficiently and doesn't incur the String creation overhead.

UPDATE: Jonas is right in the comment below. My warning holds for += but not <<.

nertzy
*increasingly long strings* - will it really? `+=` has that problem but `<<` is an append.
Jonas Elfström
That's good to know! I have updated my answer. So `<<` destructively updates the string it's called on, rather than returning a new String like `+=` does.Thanks!
nertzy
A: 

I'm using Python, but I guess the strategies are similar.

I try to use small functions/methods, so that local variables get automatically garbage collected when you return to the caller.

In larger functions/methods I explicitly delete large temporary objects (like lists) when they are no longer needed. Closing resources as early as possible might help too.

unbeknown
That's a solution, but in Ruby there's no way to explicit delete an object. There aren't even any destructors. Sometimes the Ruby-way is a bit strange. ;)
unexist