Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ruby tend to assign object IDs in descending order?

I've noticed that objects have their IDs assigned in a counterintuitive fashion. The earlier an object is created, the greater its object ID. I would have thought they would have been assigned in ascending order, rather than the other way around.

For example:

obj1 = Object.new
obj2 = Object.new
obj3 = Object.new

p obj1.object_id # => 4806560
p obj2.object_id # => 4806540
p obj3.object_id # => 4806520

Why are they assigned in such a way and also why is there a step of 20, rather than 1 in code run by the Ruby interpreter, but a vastly greater difference between object IDs for code run by Ruby's irb?

like image 782
Matty Avatar asked May 03 '12 00:05

Matty


3 Answers

Handwaving over many details, ruby allocates a chunk of the heap to put objects in:

1 | 2 | 3 | 4 | 5

Then traverses them in-order and adds them to a linked-list of free objects. This causes them to be in reverse order on the linked-list:

freelist → NULL
freelist → 1 → NULL
freelist → 2 → 1 → NULL
freelist → 3 → 2 → 1 → NULL
freelist → 4 → 3 → 2 → 1 → NULL
freelist → 5 → 4 → 3 → 2 → 1 → NULL

When allocating an object ruby uses the first item on the linked list:

object = freelist
freelist = object.next_free

So the freelist now looks like:

freelist → 4 → 3 → 2 → 1 → NULL

and further allocated objects will appear in reverse order across small allocations.

When ruby needs to allocate a new chunk of heap to store more objects you'll see the object_id jump up then run down again.

like image 190
drbrain Avatar answered Nov 18 '22 16:11

drbrain


The Ruby interpreter is a C program, you are probably looking at the corresponding memory addresses of the objects.

like image 42
Erik Petersen Avatar answered Nov 18 '22 16:11

Erik Petersen


For what it's worth, you can see a totally different progression on different implementations; everyone allocates their objects in a different way, with different-sized buckets.

MRI 1.9.3

objs = [Object.new, Object.new, Object.new]
objs.each {|o| puts o.object_id}
# 70257700803740
# 70257700803700
# 70257700803680

JRUBY

objs = [Object.new, Object.new, Object.new]
objs.each {|o| puts o.object_id}
# 2048
# 2050
# 2052

RBX

objs = [Object.new, Object.new, Object.new]
objs.each {|o| puts o.object_id}
# 3920
# 3924
# 3928
like image 38
Bill Dueber Avatar answered Nov 18 '22 17:11

Bill Dueber