IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Où sont passés les octets économisés ? En-têtes d'objets compactés dans IntelliJ IDEA, l'EDI JetBrains pour le développement en Java et Kotlin

Le , par JetBrains

126PARTAGES

4  0 
IntelliJ IDEA est un environnement de développement intégré (EDI) écrit en Java pour le développement de logiciels en Java, Kotlin, Groovy et d'autres langages basés sur la JVM. Il est développé par JetBrains et est disponible sous forme d'édition communautaire sous licence Apache 2 avec une licence propriétaire pour certains plugins intégrés, ainsi que sous forme d'édition commerciale propriétaire. Les deux peuvent être utilisées pour le développement commercial.

Les agents IA permettent certes d’accélérer la production de code, mais ils ne réduisent pas le coût de son exécution. Dans les applications gérant des millions d’objets, l’allocation de mémoire et le ramasse-miettes restent des enjeux majeurs. La JVM s’attaque à ces coûts sur plusieurs fronts : ZGC cible les pauses de collecte des déchets, le projet Leyden s’attaque au démarrage et à la phase de préchauffage, et le projet Lilliput réduit l’espace occupé par chaque en-tête d’objet. Dans Java 27, HotSpot active par défaut les en-têtes d’objets compacts (projet Lilliput, JEP 534).

Les benchmarks synthétiques montrent souvent une empreinte mémoire réduite pour les objets, mais qu’apporte réellement ce changement pour une application JVM massive en conditions réelles ? Nous avons évalué cet impact directement au sein d’IntelliJ IDEA.

Que sont les en-têtes compacts ?

Chaque objet Java comporte un en-tête. Celui-ci identifie la classe de l’objet et contient ou encode l’état utilisé pour le verrouillage, le hachage d’identité et le ramasse-miettes. Comme chaque instance subit ce surcoût, la réduction de la taille de l’en-tête constitue un objectif intéressant pour l’optimisation de la mémoire.

Dans la configuration HotSpot 64 bits que nous avons testée, un en-tête d’instance classique occupe 12 octets : un mot de marque de 8 octets et un pointeur de classe de 4 octets. La structure compacte compresse ces deux éléments en un seul mot de 8 octets.

Cela se traduit-il par une réduction immédiate de 10 % à 20 % de la mémoire totale de l’application ? Pas nécessairement. Ci-dessous, nous suivons l’impact des en-têtes compacts, depuis les structures brutes des objets et les vidages de tas jusqu’à la mémoire résidente du processus et aux charges de travail interactives de l’EDI, en mettant en évidence où les économies se concrétisent et où elles disparaissent.

Comment nous avons testé les en-têtes compacts dans IntelliJ IDEA

Nous avons mesuré trois configurations avec des besoins en ressources croissants :

- Aucun projet (uniquement l’écran d’accueil ouvert).
- Petit projet : Spring Petclinic, avec environ 3 370 lignes de code Java/Kotlin.
- Grand projet : monorepo IntelliJ IDEA, avec environ 26,5 millions de lignes de code.

Les tailles des bases de code diffèrent de près de quatre ordres de grandeur, ce qui se traduit par des profils d’indexation et des empreintes mémoire sensiblement différents. Pour les deux configurations de projet, nous avons exécuté un script automatisé qui ouvrait de manière répétée des fichiers source Java, insérait et supprimait des commentaires, résolvait les références de code, enregistrait les modifications et fermait l’éditeur.

Nous avons utilisé IntelliJ IDEA 2026.2.3 fonctionnant sur JBR 25.0.4 (le runtime Java utilisé par les IDE JetBrains), en activant et désactivant explicitement les en-têtes compacts. Dans chaque comparaison « activé/désactivé », la compilation, l’instantané du projet, la charge de travail et les valeurs -Xmx sont restés identiques. Nous avons effectué plusieurs exécutions par configuration sur un hôte par ailleurs inactif, en alternant l’ordre d’exécution « activé/désactivé » afin de minimiser le réchauffement du cache et les biais d’ordre.

Taille de l’ensemble résident (RSS)

Commençons par la métrique principale que les utilisateurs ont tendance à remarquer lorsqu’un processus semble volumineux : sa RSS (taille de l’ensemble résident).

La RSS compte les pages de RAM physique mappées à un processus. Cela inclut les pages de mémoire privée et les pages de mémoire partagée, telles que les bibliothèques dynamiques. Étant donné que les pages partagées sont comptabilisées pour chaque processus qui les mappe, la RSS ne représente pas la quantité de RAM exclusivement occupée par l’EDI.

Nous avons échantillonné le processus principal de l’IDE à l’aide de la commande `ps`. Le tableau ci-dessous répertorie la moyenne des trois médianes par phase d’exécution en MiB, les médianes minimale et maximale de chaque exécution étant indiquées entre parenthèses.


Conclusion : à des limites -Xmx identiques, la compaction des en-têtes n’a pas entraîné de réduction statistiquement significative ni constante de la mémoire RSS globale.

Cela signifie-t-il que la compaction des en-têtes n’a apporté aucun avantage concret ? Pour y répondre, il faut examiner l’intérieur du tas et comprendre comment fonctionne réellement la compaction des en-têtes, plutôt que de supposer une baisse généralisée de la mémoire du processus.

Mesure de l’empreinte des objets

La mémoire RSS d’un processus englobe bien plus que les objets actifs du tas Java. Les pages résidentes peuvent inclure de l’espace libre non alloué au sein du tas engagé, des métadonnées de classe, du code compilé par le JIT, des piles de threads, des tampons natifs et des structures de données de la JVM, des bibliothèques partagées, ainsi que d’autres fichiers mappés en mémoire. La réduction des en-têtes d’objets à elle seule ne permet donc pas nécessairement de diminuer le RSS du processus d’un nombre équivalent d’octets (à supposer qu’elle entraîne un changement perceptible).

Pour isoler les gains structurels, nous avons comparé l’empreinte des objets, c’est-à-dire le nombre d’octets qu’un même ensemble d’objets capturés occuperait selon chaque modèle de disposition d’en-tête.

Pour vos projets, vous pouvez explorer cela directement dans IntelliJ IDEA. Son vidage de mémoire affiche le nombre d’instances, les tailles superficielles et les tailles conservées par classe, ce qui vous permet de voir ce qui occupe le tas. À partir de la version 2026.3 EAP, il propose également des estimations tenant compte des en-têtes compactes à l’aide des modèles de disposition de JOL.

Le tableau ci-dessous compare les empreintes modélisées pour des tas capturés identiques.


Ici, les économies sont visibles : le modèle à population fixe réduit la taille des objets « Welcome » et « Petclinic » d’environ 6,3 %, et celle des objets « monorepo » de 8,2 % à 8,4 %. Bien que le RSS au niveau du système ait masqué cet effet, les économies de mémoire au sein du tas sont bien réelles.

Pourquoi le gain n’est-il pas constant ?

Pourquoi la suppression de 4 octets de l’en-tête ne se traduit-elle pas par un gain uniforme de 4 octets pour chaque objet ? Parce que deux tas de même taille peuvent contenir des objets très différents, et que l’en-tête n’est qu’une des variables en jeu.

Une instance stocke également des champs primitifs et des références. Un tableau stocke sa longueur et les données de ses éléments. Leur nombre, leur type et la largeur des références influent tous sur la taille globale d’un objet. Dans notre comparaison, les types de champs et la largeur des références restent identiques, mais la modification de l’en-tête peut déplacer les décalages des champs et altérer le remplissage. Voici comment cela fonctionne :

Par défaut, HotSpot arrondit la taille superficielle de chaque objet à un multiple de huit octets. Cela permet de compenser la réduction de l’en-tête de quatre octets : un objet contenant un entier (int) conserve une taille de 16 octets dans les deux dispositions (12 + 4 octets, ou 8 + 4 octets plus 4 octets de remplissage). En revanche, un objet vide passe de 16 à 8 octets ; un objet comportant deux champs de type int passe de 24 à 16 octets, comme l’illustre l’image ci-dessous :


Il peut également y avoir un remplissage à l'intérieur d'un objet. Les champs ont leurs propres exigences d'alignement ; ils ne commencent pas tous sur des limites de huit octets. Dans la disposition HotSpot modélisée ici, les décalages des champs ordinaires suivent leur largeur de stockage : un octet pour les types byte/boolean, deux pour les types short/char, quatre pour les types int/float et les références compressées, et huit pour les types long/double. Cela est distinct de l’arrondi de l’objet entier à une taille de huit octets, et ce n’est pas une garantie du langage Java.

Examinons Long.value. Après un en-tête de 12 octets, son champ de huit octets commence à l’offset 16, laissant un espace de quatre octets. Après un en-tête de huit octets, il peut commencer à l’offset 8. L’objet entier passe ainsi de 24 à 16 octets. HotSpot peut compacter ou réorganiser les champs ; les offsets exacts dépendent donc toujours de la classe et des règles de disposition de la JVM.


Les tableaux suivent le même schéma : byte[0] reste à 16 octets, tandis que byte[1] passe de 24 à 16 octets. Lorsqu’un tas contient des millions d’objets de formes différentes, le gain total dépend de cette composition, et pas seulement du nombre d’objets.


Nous avons vérifié ces exemples par rapport aux tailles « shallow » réelles à l’aide de Instrumentation.getObjectSize sur le même JBR, avec les en-têtes compactés explicitement désactivés puis activés. Une taille « shallow » prend en compte l’en-tête de l’objet, ses champs et son remplissage, mais pas les objets accessibles via ses références. Nous avons également vérifié les références compressées et l’alignement. Voici ce qu’a rapporté le JBR 25.0.4 :


La conclusion à tirer de ce tableau est simple : un en-tête plus petit ne rend pas tous les objets plus petits. La réduction totale du tas dépend entièrement du profil de population des objets d’une application.

Avantages concrets : allocation, GC et débit

Même si le RSS du processus ne diminue pas avec une valeur -Xmx fixe, la réduction de la taille des objets apporte-t-elle des avantages ailleurs lors de l’exécution ? Nous avons recherché quatre effets :

- Le même flux de travail alloue-t-il moins de mégaoctets au total ?
- Le ramasse-miettes s’exécute-t-il moins souvent ou consomme-t-il moins de cycles ?
- Les opérations scriptées de l’EDI s’accélèrent-elles grâce aux changements ci-dessus ou à des effets tels qu’une meilleure localité du cache du processeur ?
- L’application peut-elle s’exécuter de manière fiable avec une valeur -Xmx plus faible ?

Pour les trois premières questions, nous avons comparé les cas avec et sans en-têtes pour la même valeur -Xmx : 2 048 Mio pour Petclinic et 4 096 Mio pour le monorepo.

La JVM a-t-elle alloué moins d’octets ?

Pour chaque projet, notre script de benchmark a répété la même séquence d’opérations : ouverture d’un fichier Java, insertion et suppression d’un commentaire, résolution des références, enregistrement et fermeture de l’éditeur. Cela inclut les tâches concurrentes en arrière-plan et les opérations de sondage ; il ne s’agit donc pas d’un récapitulatif des allocations pour les modifications prises isolément.

Si vous souhaitez analyser les points chauds d’allocation dans votre propre code, le profileur d’allocations mémoire d’IntelliJ IDEA suit les chemins d’appel et les chronologies. À l’instar de l’outil de vidage de tas d’IntelliJ IDEA, il modélise nativement les en-têtes d’objets compacts, ce qui vous permet d’obtenir des chiffres réels pour votre application, quelle que soit la version de la JVM ciblée.


À charge de travail identique, les en-têtes compactés ont permis de réduire de 8,8 % à 13,0 % le volume total alloué.

Le ramasse-miettes a-t-il eu moins de travail ?

Une baisse du volume d’allocation pourrait signifier moins de travail pour le ramasse-miettes. Nous avons comparé le nombre de collectes du ramasse-miettes de la JVM et le temps écoulé lors de l’ouverture et de l’indexation du projet, puis les durées de pause du ramasse-miettes lors d’opérations scriptées.


Lors de l’ouverture et de l’indexation du monorepo, chaque exécution avec des en-têtes compactés a enregistré moins d’événements de ramasse-miettes que les exécutions avec des en-têtes conventionnels. La durée moyenne des collectes et l’utilisation du processeur enregistrée pour le ramasse-miettes étaient également plus faibles dans ces comparaisons.

La vitesse de fonctionnement de l’EDI s’est-elle améliorée ?

C’est en fin de compte la question qui intéresse le plus les développeurs. Pour les mêmes opérations, nous avons comparé leur durée totale, leur latence d’opération au 95e centile et l’utilisation du processeur à l’échelle du processus.


Les chiffres de performance affichent une tendance légèrement positive, suggérant un léger gain de vitesse.

Jusqu’où peut-on réduire la valeur de -Xmx ?

Si les en-têtes compacts permettent de regrouper davantage d’instances dans un même volume de tas, jusqu’où peut-on réduire la valeur de -Xmx avant que les charges de travail ne tombent en panne ?

Nous avons réduit progressivement la valeur de -Xmx jusqu’à ce que les scénarios d’indexation et d’édition ne puissent plus s’exécuter de manière totalement fiable. Un « passage réussi » signifiait que le scénario court s’était terminé, même s’il était devenu lent.

Le tableau ci-dessous présente les paramètres les plus bas testés qui ont permis de mener à bien toutes les itérations pour chaque projet et chaque disposition d’en-têtes.


Remarque : ces valeurs correspondent aux seuils limites à partir desquels les charges de travail de test ont été systématiquement menées à bien ; il ne s’agit pas de configurations recommandées pour la production.

Pour Petclinic, l’utilisation d’en-têtes compactes nous a permis de réduire la taille minimale viable du tas de 400 Mio à 376 Mio (soit une réduction de 6 %). Pour le monorepo, le plafond minimal est passé de 1 888 Mio à 1 568 Mio, soit un gain de 320 Mio (17 %).

Conclusion

Les mises à niveau qui améliorent l’efficacité des applications existantes sans nécessiter la moindre ligne de code sont rares. Les en-têtes d’objets compacts y parviennent en réduisant la charge mémoire supportée par des millions d’objets. Parallèlement aux améliorations apportées au démarrage et au ramasse-miettes, ils s’inscrivent dans le cadre plus large des travaux de Java sur l’efficacité en exécution – renforçant la JVM à une époque où nous générons plus de code que jamais.

Lorsque vous adoptez les en-têtes compacts dans vos propres applications, le bénéfice réel dépendra de la composition de vos objets et de leur alignement. De nombreux objets occuperont moins d’espace, ce qui se traduit souvent par une baisse du taux de rotation des allocations, une pression réduite sur le ramasse-miettes et un plafond de tas viable plus bas. Cependant, cela ne garantit pas une baisse immédiate et proportionnelle de la mémoire RSS du processus ni une exécution plus rapide dans tous les cas. Mesurez ces indicateurs séparément et ne réduisez pas votre paramètre -Xmx simplement parce que les en-têtes sont censés être plus petits.

Vous utilisez peut-être déjà cette fonctionnalité dans IntelliJ IDEA. La modification de la plateforme IntelliJ a été introduite dans la version 2026.2, et Java 27 active les en-têtes d’entreprise par défaut. Cela supprime toute configuration manuelle dès que vous migrez vers la nouvelle version de Java – ce qui vaut largement la peine, compte tenu de l’importance des améliorations apportées récemment au niveau du runtime.

Télécharger IntelliJ IDEA gratuitement
Vous avez lu gratuitement 1 012 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !