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

4

8

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?

Hillell answered 3/5, 2012 at 0:5 Comment(2)
object_id is just an integer that uniquely identifies an object, any particular order that you think you're seeing is purely an implementation artifact.Ankh
@theTinMan Shaving yaks? No, I'm trying to satisfy a curiosity.Hillell
G
14

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.

Girlfriend answered 3/5, 2012 at 0:38 Comment(0)
H
2

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

Hachman answered 3/5, 2012 at 0:24 Comment(0)
A
2

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
Ambit answered 3/5, 2012 at 3:59 Comment(0)
E
0

I just ran a log of object ids, and it seemed that although things went in order, garbage collection seemed to make things go the other way every once in a while.I saw jumps from like 20 to 80, so it seems to be almost random. But with the amount of internal objects that Ruby maintains, object id orders are nothing to be depended on. It could also be that Ruby starts at the top of the local platform's int or short to allow an easy test for running out (if(current_id==0) { problem }). From what i've seen from our various numbers, it seems to be completely different and non determinable. It (almost) looks to me like Ruby might even be using the pointer to the object, because that is guarantied unique, and would explain the huge gaps (20 bytes) between objects. When I look at the value returned by object_id, and look at it next to my system's native pointer size (64 bit Intel).

I just ran a test C++ program on that same system that printed out a pointer to an int. The pointer (in decimal) was 140734848324996 and the Ruby object id was 70118105405380. The numbers don't have much in common, but they are both in the same range and look something like pointers.

Of course, if someone would dig into the Ruby source and find out, that would be the definite answer. I'm trying.

Eugenaeugene answered 3/5, 2012 at 0:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.