OpenJDK Projet Lilliput

Contexte

Avez-vous passé ce tweet ? ou cette information ?

annonce projet lilliput

Lilliput est un nouveau projet qui vient d’être créé au sein d’OpenJDK. Il démarre tout juste donc il n’y a pas encore d’information sur le wiki.

Néanmoins, le but a été défini lors de la proposition de création du projet. L’objectif est de travailler sur la taille mémoire des instances, notamment sur la partie d’en-tête.

Situation actuelle

Regardons, pour la VM Hotspot 64 bits, l’en-tête d’une instance est actuellement de 128 bits en deux parties :

  • 1er bloc de 64 bits qui est appelé mark (ou lock-word) : cette zone a plusieurs utilités (Ramasse-miette générationnel, verrou de synchronisation, hascode…​)

  • 2e bloc de 64 bits qui est le pointeur vers la classe : cette zone permet de référencer la classe correspond à l’instance.

Voici la représentation :

+------------------+
| mark (Lock-word) |
+------------------+
| Class pointer    |
+------------------+
| Field 1          |
+--------+---------+
| Field 2| Field 3 |
+--------+---------+
| etc              |

Nous pouvons visualiser l’usage mémoire d’une instance en utilisant l’outil JOL (Java Object Layout) d' OpenJDK. Donc, nous allons ajouter la dépendance Maven vers JOL dans notre pom.xml.

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.15</version>
</dependency>

Nous allons commencer par écrire une classe très simple sans attribut.

public class A {
    // pas d'attributs
}

Ensuite, nous écrivons du code pour instancier notre classe, puis pour visualiser l’usage mémoire avec JOL.

    final A a = new A();

    // Utilisation de JOL
    ClassLayout layout = ClassLayout.parseInstance(a);
    out.println(layout.toPrintable());

Nous obtenons le résultat suivant :

fr.lbenoit.package.demo.test.MaClasse$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4        (object header: class)    0x08011b60
 12   4        (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

avertissement Pour l’en-tête contenant le pointeur de la classe (2e ligne), vous pourriez me dire que la taille est seulement de 4 octets et non de 8 octets. Oui et c’est normale, car j’utilise un JDK 11 et les pointeurs de classes sont compressés par défaut.

Néanmoins, avec les histoires d’alignement mémoire, la taille de l’instance reste à 16 octets.

Au niveau de la JVM, nous pouvons désactiver cette compression avec l’option -XX:-UseCompressedClassPointers, nous obtenons le résultat attendu :

org.openjdk.jol.samples.JOLSample_15_IdentityHashCode$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   8        (object header: class)    0x00007f2ee931c3b8
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

La taille est bien de 16 octets, soit 128 bits.

Maintenant, nous pouvons compléter notre classe avec deux attributs :

  • attribut bool de type boolean

  • attribut obj de type Objet

Nous obtenons le code suivant :

public class A {
    boolean bool;
    Object obj;
}

Après les en-têtes de l’instance, nous allons pouvoir visualiser les attributs de la classe. Voici le résultat en utilisant toujours l’outil JOL:

fr.lbenoit.package.demo.test.MaClasse$A object internals:
OFF  SZ               TYPE DESCRIPTION               VALUE
  0   8                    (object header: mark)     N/A
  8   4                    (object header: class)    N/A
 12   1            boolean A.bool                    N/A
 13   3                    (alignment/padding gap)
 16   4   java.lang.Object A.obj                     N/A
 20   4                    (object alignment gap)
Instance size: 24 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

En complément, pour les tableaux, nous avons la même représentation avec un champ supplémentaire avec la taille. Cela donne la représentation suivante :

+------------------+
| mark (Lock-word) |
+------------------+
| Class pointer    |
+------------------+
| Array-Length     |
+--------+---------+
| Elem 1 | Elem 2  |
+--------+---------+
| etc              |

Projet Lilliput

Donc, les objectifs sont les suivants :

  • réduire l’en-tête à 64 bits, voir potentiellement dans un second objectif 32 bits.

  • rendre l’en-tête des instances plus flexibles.

En reduisant la taille de l’entête d’une instance, cela diminuera la mémoire utilisée et la pression associée lors de forte allocation.

Donc, nous aurons les intérêts suivants :

  • Réduction de l’usage de tas (heap)

  • Taux d’allocation plus haut

  • Réduction de l’activité du ramasse-miette

avertissement Roman Kennke est leader du projet (auteur du tweet en début de billet). De plus, Rémi Forax fait parti du projet en tant que contributeur.