VASmalltalk – Objektablage im Speicher

Wenn man sich erst einmal mit dem Speicherlayout von Instanzen in VASmalltalk beschäftigt, dann geht man immer tiefer und tiefer …. ob das Sinn macht, fragt man sich nicht mehr.

Eines möchte ich gerne vorneweg sagen: ich habe keinen Zugang zum Quelltext der virtuellen Maschine. Meine Ausführungen basieren nur auf den Include-Dateien, die dem Produkt mitgegeben werden.

Definieren wir uns doch erst einmal unsere Beispielklasse:

Object subclass: #TestOne
    instanceVariableNames: 'm'
    classVariableNames: ''
    poolDictionaries: ''

und wie bereits in einem anderen Posting beschrieben, werden Instanzen dieser Klasse in der folgenden Struktur abgelegt:

typedef struct ESTestOne{
    struct ESObjectHeader* class;
    U_32 flags;
    U_32 size;
    struct ESObjectHeader* m
} ESTestOne;

Da wir mit Smalltalk eine ungetypte Sprache haben, kann in der Instanzvariable alles mögliche gespeichert werden.

Wenn man z.B. den folgenden Ausdruck betrachtet:

| one two |

one := TestOne new.
two := TestOne new.
one m: two.

bekommen wir das folgende Speicherlayout:

struct ESTestOne{
    struct ESObjectHeader* class;
    U_32 flags;
    U_32 size;
    struct ESObjectHeader* m => struct ESTestOne{
                                     struct ESObjectHeader* class;
                                     U_32 flags;
                                     U_32 size;
                                     struct ESObjectHeader* m
                                };
};

In der Speicherstelle “m” der ersten Struktur steht also die Adresse der zweiten Struktur. Nun versucht man natürlich auch Speicherplatz zu sparen und hat die sogenannten “Immediate”-Objekte erschaffen. Mit diesen Objekten versucht man möglichst viele, oft gebrauchte Instanzen abzudecken:

true, false, nil, Instanzen der Klassen Character und SmallInteger.

Wenn man Instanzen dieser Klassen hat, dann ist der Inhalt der Speicherstelle “m” nicht mehr die Adresse auf eine andere Struktur, sonder der Inhalt ist der Wert des Objektes selber.

Wenn man z.B. den folgenden Ausdruck betrachtet:

| one two |

one := TestOne new.
two := TestOne new.
one m: 1

bekommen wir das folgende Speicherlayout:

struct ESTestOne{
    struct ESObjectHeader* class;    (4 Bytes)
    U_32 flags;                      (4 Bytes)
    U_32 size;                       (4 Bytes mit Anzahl der Bytes, die nach diesem Eintrag noch folgen)
    struct ESObjectHeader* m         (4 Bytes, die den Wert 1 repräsentieren)
};

Woran kann nun die virtuelle Maschine dieses erkennen ? Nun ja, die beiden “unteren” Bits des Wertes “m” werden gesondert interpretiert. Ist der Wert dieser Bits ungleich “0” (Null), dann handelt es sich um ein Immediate-Objekt. Wenn diese den Wert “0” haben, dann handelt es sich um einen Pointer.

Daraus kann man schliessen, dass jedes “non”-Immediate Objekt im Speicher auf 4er-Byte Grenzen liegen muss.

Betrachtet man die untersten beiden Bits, kommt man zu folgenden Aussagen:

00 = non-immediate Objekt
01 = Immediate-Objekt mit Interpretation “SmallInteger”
10 = Immediate-Objekt mit anderer Interpretation (Character, Boolean , UndefinedObject)
11 = Immediate-Objekt mit Interpretation “SmallInteger”

Um die “anderen” Interpretation zu beschreiben, benötigt man die beiden nächsten Bits:

11 10 = Immediate-Objekt mit Interpretation “Boolean , UndefinedObject”
01 10 = Immediate-Objekt mit Interpretation “Character”

Dann kommt noch die Unterscheidung von Boolean und UndefinedObject und wir betrachten die nächsten beiden Bits:

00 11 10 = Immediate-Objekt mit Interpretation “Nil”
01 11 10 = Immediate-Objekt mit Interpretation “true”
10 11 10 = Immediate-Objekt mit Interpretation “false”.

Aus diesen Daten kann man sehen, dass für Instanzen der Klasse SmallInteger 31 Bits zur Verfügung stehen. Damit kann man einen Zahlenbereich von -1073741824 bis 1073741823 abdecken.

Ähnlich sieht bei Instanzen von Character aus – jedoch mit einem eingeschränkten Datenbereich, da man hier ja 4 Bits abzehen muss für die Interpretation der “anderen” Immediate-Objects. Es bleiben also 28 Bits für den Wert der zu speichernden Characters.

Das eigentliche Speichern sollte auch klar sein: der eigentliche Wert wird nach Typ entsprechend nach links geschiftet und mit den notwendigen Tags versehen, damit es als Immediate-Objekt erkannt wird.

Damit schliessen wir den Bereich der Immediates ab und betrachten nun noch die “normalen” Objekte. Hier fällt natürlich das Feld “flags” auf:

Hier sind noch einige zusätzliche Werte definiert:

* es handelt sich um ein ReadOnly Objekt
* es handelt sich um ein indexable Object (z.B. instanzen von Array)
* es handelt sich um ein Weakable Objekt
* es handelt sich um ein Objekt im FixedSpace
* handelt es sich bei den Objektdaten um Pointer auf Objekte (Instanzvariablen) oder sind dort direkt Daten im Byte ( 8 bit), Word (16 bit) oder Long (32 bit) Format

This entry was posted in Smalltalk and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s