eigenclass logo
MAIN  Index  Search  Changes  PageRank  Login

Memory overhead for small objects: often underestimated

update.png Added information about the cost of a Symbol (also heavier than you might have expected), and the RStruct optimization in 1.9.

It is well known that the minimal space overhead for a non-immediate object in Ruby is 20 bytes*1. But the overhead will be much higher if your object happens to have instance variables (read: almost always).

Take for instance

class Stupid; attr_accessor :foo end
class Stupid2; attr_accessor :foo, :bar end

o1 = Stupid.new
o1.foo = 1
o2 = Stupid2.new
o2.foo = 1
o2.bar = 2

o1 will take around 20 + 16 + 11 times 4 + 4 times 4 + 3 times roman MALLOC_OVERHEAD = 108/120 and o2 20 + 16 + 11 times 4 + 2 times 4 times 4 + 4 times roman
MALLOC_OVERHEAD = 128/140 bytes, i.e. for objects with under 55 instance variables, the memory footprint is around 100 bytes plus 20 bytes per instance variable --- that's quite heavy.

The exact numbers depend on the behavior of malloc regarding memory alignment and overhead. Doug Lea's malloc, in wide use, takes 8 or 4 bytes per chunk.

Here are some other overheads for Arrays, Hashes, Symbols, etc. (+M denotes the malloc overhead per allocated chunk, normally 4 or 8 bytes):

  • basic RVALUE overhead: 20 bytes
  • Array: add 16 * 4 (+M) [ptr] (up to 16 elements)
  • object with instance variables: add 16(+M) [st_table] + 11 * 4 (+M) [bins] + 4 * 4 (+M) [st_table_entry] (bins: constant up to 55 instance variables; add 4 * 4 (+M) per element)
  • Hash: add 16(+M) + 11 * 4 (+M) + 4 * 4 (similar to the iv_tbl overhead)
  • Struct: add 4 (+M) per "attribute"
  • update.png a Symbol takes sym.to_s.size + 1 (typically padded to 8 bytes, with a minimum of 16 bytes) plus 2 * (16 (+M)) (minimum), e.g. after you do "foo".to_sym, ~56 bytes are allocated, never to be released

update.png In Ruby 1.9, up to 3 attributes can be packed in the internal RStruct structure, which means that in

 Foo = Struct.new(:a, :b, :c)
 a = Foo.new(1,2,3)

the object referenced by a takes only 20 bytes, instead of 44/68 bytes as in previous versions. Compare that cost (20 bytes in 1.9, ~50 in 1.8) to that of a normal object with 3 instance variables: around 160 bytes, that is 3 to 8 times heavier!


n - s (2006-07-17 (Mon) 19:19:27)

s


*1 all the values given here correspond to a 32bit platform