How Does Object_Id Assignment Work

How does object_id assignment work?

In MRI the object_id of an object is the same as the VALUE that represents the object on the C level. For most kinds of objects this VALUE is a pointer to a location in memory where the actual object data is stored. Obviously this will be different during multiple runs because it only depends on where the system decided to allocate the memory, not on any property of the object itself.

However for performance reasons true, false, nil and Fixnums are handled specially. For these objects there isn't actually a struct with the object's data in memory. All of the object's data is encoded in the VALUE itself. As you already figured out the values for false, true, nil and any Fixnum i, are 0, 2, 4 and i*2+1 respectively.

The reason that this works is that on any systems that MRI runs on, 0, 2, 4 and i*2+1 are never valid addresses for an object on the heap, so there's no overlap with pointers to object data.

Why does 1.object_id always return 3 in Ruby 2.5.1?

The object ID is calculated from the objects value plus some additional information. From that calculation you can derive the values you are seeing in your examples.

for more information in details read these SO post:

  • How does object_id assignment work?
  • Why is the object_id of a Fixnum an odd number?

Ruby object to_s what is the encoding of the object id?

Think of the object_id, or __id__ as the "pointer" for the object. It is not technically a pointer, but does contain a unique value that can be used to retrieve the internal C VALUE.

There are patterns to the value it has for some data types, as you can see with its hexadecimal representation with to_s. I am will not go into all the details, as there are already numerous answers on SO explaining, and already linked from comments, but integers (up to a FIXNUM_MAX, have predictable values, and special constants like true, false, and nil will always have the same object_id in every run.

To put simply, it is nothing more than a number, and shown as a hexadecimal (base 16) value, not any actual "encoding" or cypher.

Going to expand upon this a bit more in light of your latest edits to the question. As you posted, the hexadecimal number you see in to_s is the value of the internal C VALUE of the object. VALUE is a C data type (unsigned, pointer size number) that every Ruby object is represented as in C code. As @Stefan pointed out in a comment, for non-integer types (I speak only for MRI version), it is twice the value of the object_id. Not that you probably care, but you can shift the bits of an integer to predict the value for those.

Therefore, using you example.

A value of 0x00007fac5eb6afc8 is simple hexadecimal notation for a number. It uses a base 16 counting system as opposed to the base 10 decimal system we are more used to in everyday life. It is simply a different way of looking at the same number.

So, using that logic.

a = 0x00007fac5eb6afc8
#=> 140378300133320 # Decimal representation

a /= 2 # Remember, non-integers are half of this value
#=> 70189150066660 # Your object_id

object_id of keys (and some other objects) are always the same. Why is that?

So i searched over the internet and found out this article . I come to know that ruby process maintains a symbol table which has one entry per symbol as long as the process exists, so whenever a new symbol is created ruby do a search in that symbol table and if not exists it creates a new one to the last entry just like the entries in database tables.

More sources that can help:

Id2sym & symbol.object_id

Why was the object_id for true and nil changed in ruby2.0?

A look at the Ruby source where these values are defined suggests that this has something to do with “flonums” (also see the commit where this was introduced). A search for ”flonum” came up with a message on the Ruby mailing list discussing it.

This is a technique for speeding up floating point calculations on 64 bit machines by using immediate values for some floating point vales, similar to using Fixnums for integers. The pattern for Flonums is ...xxxx xx10 (i.e. the last two bits are 10, where for fixnums the last bit is 1). The object_ids of other immediate values have been changed to accomodate this change.

You can see this change by looking at the object_ids of floats in Ruby 1.9.3 and 2.0.0.

In 1.9.3 different floats with the same value are different objects:

1.9.3p385 :001 > s = 10.234
=> 10.234
1.9.3p385 :002 > t = 10.234
=> 10.234
1.9.3p385 :003 > s.object_id
=> 2160496240
1.9.3p385 :004 > t.object_id
=> 2160508080

In 2.0.0 they are the same:

2.0.0p0 :001 > s = 10.234
=> 10.234
2.0.0p0 :002 > t = 10.234
=> 10.234
2.0.0p0 :003 > s.object_id
=> 82118635605473626
2.0.0p0 :004 > t.object_id
=> 82118635605473626

Difference on working of object_id method with Fixnum object and String object

Fixnums are immutable objects in Ruby. There is exactly one instance created and you work with that object "directly". i.e references are not used unlike other regular objects. So They have a fixed object_id. This is ok because you have only one instance of the object.

But when you write "hello", a new string object is created. And in the same script, if you give another "hello", even though they have same content, a new object is created. Hence the different object_ids.

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

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.

Why is the `object_id` of a `Fixnum` an odd number?

This is done so that integers do not take up all the room for other objects. In Ruby all other Objects have even object_id's, they go in between. The integer object_id's are very easily converted to their value: the last bit (always a 1) is chopped off.

Integers are a bit fake objects, they are no more than an id and a shared list of methods.

Can we access the objects we created in ruby using their object _ids in ruby?

How an object ID is “assigned” depends on the Ruby implementation and other factors like the OS bitness. For example, in CRuby nil.object_id returns 4 on 32-bit and 8 on 64-bit.

Additionally nil is a so called immediate value. true, false, fixnums (small integers) and sometimes even floats are other immediate values. They have fixed IDs for the following reasons:

  • they're passed by value and not by reference like the other (dynamically allocated) objects
  • there's only one nil, one true, one 19, etc. however there can be two different arrays

See the documentation of BasicObject#object_id. You can also click to toggle the source to get a look at the CRuby implementation.

Call ObjectSpace._id2ref to retrieve an object by ID:

id = nil.object_id
ObjectSpace._id2ref(id) # => nil

In some implementations that method may not be implemented or really slow. According to matz it was originally a hack needed to implement weakref but current versions don't call it anymore.

Related Topics

Leave a reply