Friday, April 17, 2009

Why Has the Size of TObject Doubled In Delphi 2009

Because it has a new feature.


Mason Wheeler noticed that TObject.InstanceSize returns 8 (bytes). It turns out that this is new in Delphi 2009; in previous releases, TObject.InstanceSize returned 4. But when you look at the definition of TObject in System.pas, you don’t see any fields declared at all.


Four out of the eight bytes are consumed by the VMT; this has been true since the first version of Delphi. You can read more about that in this chapter from Delphi In A Nutshell, which is old, but still accurate insofar as the VMT is concerned.


But what about the remaining four bytes? I didn’t remember the answer, but I enjoy a good puzzle, so I took a closer look at the source code for TObject. While poking around in System.pas, I happened to notice the following constants:



{ Hidden TObject field info }
hfFieldSize = 4;
hfMonitorOffset = 0;


OK, that explains the four bytes, but what is this used for? Searching for hfFieldSize yielded the answer:



class function TMonitor.GetFieldAddress(AObject: TObject): PPMonitor;
begin
Result := PPMonitor(Integer(AObject) + AObject.InstanceSize - hfFieldSize + hfMonitorOffset);
end;


So any object can have a reference to a TMonitor instance, and that reference is as big as a pointer on the target CPU. That reminded me of Allen Bauer’s long series of blog posts last year on the "Delphi Parallel Library," which discuss the TMonitor class in detail. He notes:


The TMonitor class [...] is now tied directly to any TObject instance or derivative (which means any instance of a Delphi class). [...] For Tiburón [a.k.a. Delphi 2009], you merely need to call System.TMonitor.Enter(<obj>); or System.TMonitor.Exit(<obj>); among the other related methods.


The ability to lock any object is a good feature to have, especially in a multi-CPU, multi-core world. So I’m happy to spend the extra four bytes to get this feature. But I do find the implementation a bit mysterious. Why not just declare an actual field in TObject? I guess one reason would be to reduce the chance of a conflict with existing code. Another possible reason would be to enforce TMonitor’s contract; by obfuscating access to the field, you reduce the chances that an uninformed programmer performs actions on the field which break the contract. But these are just guesses. I don’t know why the Delphi team settled on this implementation. I presume they have some good reason.

No comments: