BLOG

Décryptage technique 010|Java 27 : trois nouvelles fonctionnalités disséquées jusqu'au code source — en-tête d'objet à 64 bits, pattern matching sur types primitifs, concurrence structurée

Kael Zhang
JavaJVMOpenJDK
广告 · Advertisement

Le 15 septembre 2026, le JDK 27 est officiellement sorti (GA, build 35), apportant 9 JEP. La plupart des comptes rendus s’arrêtent au niveau du communiqué de presse : un numéro, une phrase de résumé, un extrait de code. Cet article procède autrement — il colle directement le code source. Cette version compte trois fonctionnalités, chacune située à un niveau différent : JEP 534 En-tête d’objet compact (runtime HotSpot) compresse l’en-tête d’objet sur les architectures 64 bits, le ramenant de 96 bits à 64 bits ; JEP 532 Pattern matching sur types primitifs (javac) permet à instanceof et switch de prendre en charge tous les types primitifs ; JEP 533 Concurrence structurée (API java.base) regroupe un ensemble de tâches de threads associées dans une portée refermable. Cet article dissèque six choses : ce que c’est, le mécanisme central suivi jusqu’aux numéros de ligne du code source, l’évaluation technique, si cela vaut la peine de suivre, comment les activer, et le chemin qui mène de la lecture du code source à la soumission de patchs à OpenJDK.

1. De quoi s’agit-il

Commençons par dresser le tableau complet. JDK 27 compte 9 JEP, vérifiés sur la page officielle du projet : JEP 523 G1 par défaut dans tous les environnements, JEP 527 échange de clés hybride post-quantique TLS 1.3, JEP 531 Lazy Constants (3e préversion), JEP 532 filtrage par motifs pour les types primitifs (5e préversion, au cœur de cet article), JEP 533 concurrence structurée (7e préversion, au cœur de cet article), JEP 534 en-têtes d’objets compacts activés par défaut (fonctionnalité finale, au cœur de cet article), JEP 536 masquage des données in-process de JFR, JEP 537 Vector API (12e incubation), JEP 538 encodage PEM (3e préversion). À noter : la numérotation n’est pas contiguë — pas de 524–530 ni de 535.

Les trois fonctionnalités ciblées couvrent chacune un niveau différent, et leurs degrés de maturité varient fortement. JEP 534 est une fonctionnalité finale, activée par défaut dans JDK 27 ; elle modifie la disposition mémoire des objets dans HotSpot, de façon totalement transparente pour le code Java. Historique : introduite à titre expérimental dans le JEP 450 (JDK 24), promue au rang de fonctionnalité finale sans activation par défaut dans le JEP 519 (JDK 25), activée par défaut dans le JEP 534 (JDK 27) ; l’Owner est Roman Kennke. JEP 532 est une fonctionnalité de langage en préversion, qui nécessite --enable-preview ; elle règle une maladresse accumulée depuis plus de vingt ans : le filtrage par motifs, instanceof et switch n’ont longtemps reconnu que les types références. JEP 533 est une fonctionnalité d’API en préversion, qui exige elle aussi --enable-preview ; elle arrache un « groupe de tâches apparentées » à l’état de liberté des pools de threads pour en faire des unités de travail aux limites clairement définies. Les Authors sont Alan Bateman, Viktor Klang et Ron Pressler, et le chantier se poursuit depuis le JEP 428 (incubé dans JDK 19) jusqu’à aujourd’hui.

Un fil conducteur relie les trois : économiser la mémoire, alléger le compilateur, rendre la concurrence facile à gérer, tout relève du pragmatisme d’ingénieur — aucun nouveau paradigme, il s’agit uniquement de rembourser de vieilles dettes.

II. Mécanismes fondamentaux

Explicitons d’abord les règles de citation : « la version JEP » provient des pages JEP officielles d’openjdk.org ; « ce que montre le code source » correspond au texte original du dépôt openjdk/jdk sous le tag jdk-27+35, avec chemins de fichiers et numéros de ligne.

2.1 JEP 534 : 96 bits réduits à 64 bits, le schéma de bits de markWord.hpp est la réponse complète

Sur architecture 64 bits, l’ancien en-tête d’objet se composait du mark word (64 bits) plus du class word (32 bits lorsque les compressed class pointers sont activés), soit 96 bits au total. L’idée du JEP 450 consiste à supprimer la frontière entre les deux segments et à faire entrer le pointeur de classe compressé dans le mark word. Schéma de disposition officiel (texte original du JEP 450) :

Header (compact):
64                    42                             11   7   3  0
[CCCCCCCCCCCCCCCCCCCCCCHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHVVVVAAAASTT]
(Compressed Class Pointer)      (Hash Code)        /(GC Age)^(Tag)
                             (Valhalla-reserved bits)(Self Forwarded Tag)

Le pointeur de classe est en outre comprimé à 22 bits, la taille du hash code reste inchangée, et 4 bits sont réservés au Project Valhalla. C’est la version JEP ; le code source ci-dessous s’y aligne bit à bit. Schéma de bits du commentaire d’en-tête de markWord.hpp L43–49 :

//  64 bits (without compact headers):
//  unused:22  hash:31  valhalla:4  age:4  self-fwd:1  lock:2
//  64 bits (with compact headers):
//  klass:22   hash:31  valhalla:4  age:4  self-fwd:1  lock:2

En une phrase : les 22 bits de poids fort, autrefois unused, accueillent désormais klass ; les cinq autres champs de bits ne bougent pas d’un iota, et le total tombe juste à 64 bits. Définitions des constantes (même fichier, L116–154, lignes clés extraites) :

  static const int lock_bits        = 2;
  static const int self_fwd_bits    = 1;
  static const int age_bits         = 4;
  static const int hash_bits        = max_hash_bits > 31 ? 31 : max_hash_bits;
  // Used only with compact headers: the (narrow) Klass* lives in bits 43 to 64.
  static constexpr int klass_bits   = 22;

Trois détails : le hash est explicitement plafonné (cap) à 31 bits, en cohérence avec le « hash code de taille inchangée » du JEP 450 ; le narrow Klass de 22 bits se situe aux bits 43–64 ; les champs de bits se succèdent du poids faible au poids fort : lock(2)→self-fwd(1)→age(4)→réservé à valhalla (4)→hash(31)→klass(22) — commentaires, constantes et somme, tout se tient. Côté flags (globals.hpp L131–132) :

  product(bool, UseCompactObjectHeaders, true,
          "Use compact 64-bit object headers in 64-bit VM")

Un product flag officiel pour les plateformes LP64, à true par défaut. Rappel historique : à l’époque du JEP 450, c’était un drapeau expérimental qui nécessitait -XX:+UnlockExperimentalVMOptions ; dans JDK 27, ce n’est plus qu’une simple ligne de product flag. Le même fichier précise en L150 que la valeur est toujours false sur une VM 32 bits. Pour revenir à l’ancien layout, utilisez -XX:-UseCompactObjectHeaders ; l’ancien layout 96 bits reste présent dans cette version, et le JEP 534 liste explicitement la suppression de l’ancien layout parmi ses non-objectifs. Limites du périmètre : les descriptions de mécanismes comme « les opérations de verrouillage n’écrasent plus le mark word » ou « le transfert GC ajoute un tag self-forwarded » proviennent de la documentation du JEP 450 ; self_fwd_bits est confirmé au niveau des commentaires ; le code C++ concret d’ObjectMonitor et du transfert GC sort du cadre de cet article et ne sera pas détaillé.

2.2 JEP 532 : pattern matching sur les types primitifs — ce que fait javac à la phase de lowering

Trois limitations historiques (au sens du JEP) : le pattern matching de switch ne prend pas en charge les motifs de types primitifs ; les composants de types primitifs des record patterns doivent être de types strictement identiques (JsonNumber(double a) ne peut pas s’écrire JsonNumber(int age)), alors que le reste du langage dispose du widening automatique ; instanceof ne prend en charge que les types référence. Le JEP 532 lève les trois d’un coup et étend le sélecteur de switch à long/float/double/boolean. Le cœur sémantique est l’exactness — une conversion est exact dès lors qu’elle ne perd aucune information ; savoir si long→int ou int→float est exact dépend de la valeur d’entrée à l’exécution, ce qui exige un test à l’exécution ; unconditionally exact, en revanche, peut être affirmé dès la compilation comme ne perdant jamais d’information (deux catégories : type-based et value-based) ; les règles de dominance et d’exhaustiveness s’étendent en conséquence, et les constantes de case en virgule flottante sont dédoublonnées selon l’équivalence de représentation. Exemple officiel (texte original du JEP 532) :

switch (x.getStatus()) {
    case 0 -> "okay";
    case 1 -> "warning";
    case 2 -> "error";
    case int i -> "unknown status: " + i;   // 原 default 分支
}
int i = 1000;
if (i instanceof byte b) { ... }      // false,不进入分支
float f = 1000.0f;
f instanceof int;                     // true (exact)

Ce que montre le code source (TransPatterns.java). Le test instanceof sur types primitifs est réécrit à la phase de lowering, L200–221 :

    // $expr instanceof $primitiveType
    // =>
    // $expr instanceof T $temp && $temp instanceof $primitiveType
    if (tree.erasedExprOriginalType!=null && ...) {
        BindingSymbol temp = new BindingSymbol(Flags.FINAL | Flags.SYNTHETIC, ...);
        // 先对擦除前类型做绑定模式匹配,再对临时变量做原始类型测试
        result = translate(resultExpr);   // 两段式 && 复合表达式
    }

Une expression expr instanceof int ne produit pas directement un bytecode de test de type primitif, mais une opération ET en deux temps : on recueille d’abord la valeur dans une variable temporaire synthétique FINAL | SYNTHETIC (son nom embarque syntheticNameChar, donc aucune collision avec les variables utilisateur), puis on demande « cette conversion a-t-elle perdu de l’information ? ». Le premier temps a pour rôle de ramener la valeur en toute sécurité, le second est le jugement d’exactness à l’exécution. makePrimitive, en L772–828, apporte l’autre moitié de la réponse : le type primitif obtient son objet Class via la méthode bootstrap de constante ConstantBootstraps.primitiveClass (condy), et la signature est assemblée par PrimitiveGenerator — le compilateur génère l’invokedynamic et les matériaux du pool de constantes pour le motif de type primitif. À noter aussi L510 : lorsque le sélecteur est un type primitif, la vérification null est exemptée (un type primitif n’a pas de null) ; en L935, la vérification null du motif de liaison se dédouble selon que le type est primitif ou non. Limite du périmètre : les règles complètes d’exactness, de dominance et d’exhaustiveness ne sont tirées que de la documentation des JEP ; l’implémentation à la compilation présente dans Attr.java et Check.java sort du champ de cet article.

2.3 JEP 533 : concurrence structurée, une interface sealed et une machine à états à trois états

La concurrence structurée traite un ensemble de tâches liées comme une seule unité de travail : les sous-tâches s’exécutent par défaut sur des threads virtuels ; fork/join/close ne peuvent être appelés que par le thread propriétaire (owner), toute violation lève une StructureViolationException ; l’échec d’une sous-tâche annule par court-circuit les autres ; si le propriétaire est interrompu, la portée est fermée et toutes les sous-tâches annulées ; les sous-tâches héritent des ScopedValue ; le thread dump JSON présente l’arborescence hiérarchique des tâches (les comportements à l’exécution ci-dessus suivent tous la description du JEP). Les cinq changements de cette version (JEP 533 History) : l’interface et Joiner gagnent un troisième paramètre de type R_X (le type d’exception levée par join()) ; ajout de open(UnaryOperator) ; les trois fabriques telles que allSuccessfulOrThrow() font que join() lève une ExecutionException, chacune gagnant une surcharge avec Function ; suppression de Joiner.awaitAll() ; onTimeout() est remplacé par timeout(), l’exception de timeout ayant CancelledByTimeoutException comme cause. Exemple officiel (texte original du JEP 533) :

try (var scope = StructuredTaskScope.open()) {
    Subtask<String> user = scope.fork(() -> findUser());
    Subtask<Integer> order = scope.fork(() -> fetchOrder());
    scope.join();
    return new Response(user.get(), order.get());
}

Ce que montre le code source (StructuredTaskScope.java, fichier de 1469 lignes au total, classe d’implémentation StructuredTaskScopeImpl). L373–421 :

public sealed interface StructuredTaskScope<T, R, R_X extends Throwable>
        extends AutoCloseable
        permits StructuredTaskScopeImpl {
    sealed interface Subtask<T> extends Supplier<T> permits StructuredTaskScopeImpl.SubtaskImpl {
        enum State { UNAVAILABLE, SUCCESS, FAILED }

Deux sealed verrouillent la structure : scope ne permit que la classe d’implémentation privée au package, et Subtask ne permit que SubtaskImpl. Durant la phase d’incubation, cette API résidait dans jdk.incubator.concurrent ; sa position officielle en JDK 27 est java.util.concurrent dans java.base — de la sortie de l’incubateur à la préversion, l’appartenance au module trace la feuille de route de l’évolution. La machine à états de Subtask ne compte que trois états, et avec extends Supplier<T>, get() renvoie directement le résultat, sans tout le fatras de Future. Les méthodes par défaut de Joiner (L572–618) inscrivent la discipline d’état dans le code : onFork exige que la sous-tâche soit UNAVAILABLE, onComplete exige qu’elle ne le soit plus ; ces deux assertions encadrent strictement ce que les callbacks peuvent observer. Les valeurs par défaut en trois niveaux de la fabrique open (L1268–1269) : le open() sans argument passe par Joiner.awaitAllSuccessfulOrThrow() — l’échec d’une seule sous-tâche vaut échec global ; la javadoc (L1173–1176) précise que la configuration par défaut crée des threads virtuels non nommés, sans timeout. Toutes les API publiques du fichier portent @PreviewFeature(STRUCTURED_CONCURRENCY) — au niveau du code source, nouvelle confirmation qu’il s’agit toujours d’une API en préversion. Limites du périmètre : le point de création des threads virtuels, les appels d’interruption assurant la propagation de l’annulation et le mécanisme de temporisation du timeout relèvent de la classe d’implémentation ; le présent article n’en cite pas le code interne.

3. Évaluation technique

Posons d’abord clairement la forme des preuves : toutes les citations du code source proviennent du tag jdk-27+35 ; tous les chiffres de performance proviennent de citations des pages officielles des JEP, donc relèvent de la communication officielle, et non de tests indépendants.

JEP 534 : mécanisme prouvable, gains à prendre sur parole officielle. Sur le plan du mécanisme, les annotations de disposition des bits, les constantes de champs de bits et le commutateur par défaut s’imbriquent entre eux à trois endroits du code source ; le schéma de compression est entièrement auto-cohérent dans le code — c’est là une preuve matérielle. Sur le plan des gains, chiffres cités par l’officiel : dans un scénario, SPECjbb2015 avec 22 % d’occupation du heap en moins et 8 % de temps CPU en moins ; dans un autre, 15 % de cycles de GC en moins (autant pour G1 que pour Parallel) ; un benchmark de parsing JSON fortement parallèle 10 % plus rapide. L’appui vient là aussi de l’exposé du JEP lui-même : des centaines de services de production chez Amazon l’utilisent (pour la plupart backportés vers JDK 21/17), et le SapMachine de SAP l’active par défaut. Les risques sont clairement écrits : 4 bits sont réservés pour Valhalla ; si cela ne suffit pas, on pourra encore compresser les pointeurs de classe et l’identity hash code (selon les termes du JEP 450). Pour tempérer : klass_bits est codé en dur à 22, et la borne d’adressage de l’espace des classes se trouve encore réduite — un arbitrage explicite : de la largeur de bits contre l’activation par défaut.

JEP 532 : sémantique stable, toujours en preview. Cinquième preview, et cette version revient en preview sans aucun changement par rapport au JDK 26 (JEP 530) : le signal, c’est que la sémantique a pratiquement convergé ; mais ce « 5e passage » montre en soi qu’elle n’a pas été promue au statut définitif, la syntaxe conserve une marge d’ajustement, et un déploiement massif en production comporte un risque de reprise du code. Côté implémentation, un lowering en deux phases plus le recours à condy pour récupérer la Class montrent que le compilateur n’y va pas à la légère en interne — faire entrer les types primitifs dans le pattern matching a un coût : javac génère une couche intermédiaire supplémentaire.

JEP 533 : la surface de l’API converge. Sur sept previews, les parties les plus souvent remaniées — mode de construction, stratégies de complétion, mécanismes de timeout — aboutissent dans le JDK 27 à trois actions : R_X, plus open(UnaryOperator), plus timeout(), forme typique d’une phase de convergence. L’interface sealed verrouille les implémentations, la machine à états est réduite à trois états : la retenue du design se voit. Pour tempérer : ce 7e passage en preview signifie qu’aucune compatibilité pérenne n’est promise ; les mécanismes internes de propagation de l’annulation et des timeouts résident dans les classes d’implémentation, et l’évaluation ne peut que s’arrêter au niveau de la sémantique de l’interface.

Vue d’ensemble. L’échelle de maturité est claire : le 534 s’obtient dès la mise à niveau ; le 532 et le 533 exigent d’activer l’option de preview — aucun des trois ne devrait entrer dans un chemin critique exigeant une stabilité à long terme. Aucun des trois n’introduit de concept nouveau — les en-têtes compacts sont une réutilisation de bits, le pattern matching sur les types primitifs complète le pattern avec le widening, la concurrence structurée transpose le principe de structuration à la concurrence — l’autre face de l’esprit d’ingénieur : aucune surprise, aucune magie.

IV. Jugement de valeur

Les vrais problèmes sont tous bien réels : le surcoût des en-têtes d’objets est décrié depuis des années sur les tas 64 bits ; l’exclusion des types primitifs par instanceof et switch est une vieille blessure dans la cohérence du langage ; la gestion des erreurs et la propagation des annulations dans le code concurrent comptent parmi les zones où le taux d’incidents est le plus élevé chez les programmeurs Java. Chacun des trois JEP vise un vrai problème.

L’IA écrit de plus en plus de code, et pourtant la valeur de la lecture du code source par les humains ne cesse de monter — disons-le en passant : une fois qu’une grande partie du code est générée par des modèles, où va le temps économisé par les humains ? L’une des destinations au meilleur rapport coût-efficacité consiste à descendre d’un niveau de lecture : voir en quel bytecode le compilateur traduit la nouvelle syntaxe, quels bits le runtime modifie dans l’en-tête d’objet. Le compilateur et le runtime ne deviennent pas plus simples sous prétexte que vous ne les avez pas lus ; plus les fonctionnalités du langage s’accumulent, plus la distance entre « ce qui se passe réellement dans les sources » et « le code que vous écrivez » s’allonge. expr instanceof int, réécrit en une forme à deux étapes — motif de liaison plus opération ET —, en est un exemple : à ne voir que le sucre syntaxique, on croit à un simple test ; c’est en lisant TransPatterns qu’on découvre qu’il s’agit d’une récupération de type suivie d’une vérification d’exactness à l’exécution.

Économiser de la mémoire a un intérêt direct pour le déploiement des services d’IA, mais n’exagérons rien : pour les services d’inférence, les services de recherche et les passerelles Agent qui tournent sur la JVM, chaque baisse de l’occupation du tas se traduit par autant d’économies sur la facture cloud ; réduire l’en-tête d’objet d’un tiers est une direction assurément favorable pour les services à forte densité d’objets ; mais une « direction » ne veut pas dire que « votre service, c’est exactement ces 22 % » — le gain dépend de la distribution des tailles d’objets et du taux d’allocation.

Fixons clairement les limites : 532 et 533 sont en preview ; le JDK 28 pourra les retoucher, voire les remettre en preview — pour le code de production, on attendra la version finale ; 534 est activé par défaut mais réversible — si des problèmes de compatibilité surviennent avec des outils de monitoring ou des agents natifs, l’interrupteur de repli reste disponible ; les informations de performance et d’endossement de cet article reprennent toutes la version officielle : conservez ce même cadrage lorsque vous les citez. Quand cela vaut la peine de suivre : si vous maintenez des services JVM, que la facture mémoire vous préoccupe ou que vous écrivez du code intensivement concurrent, activez dès maintenant --enable-preview et familiarisez-vous avec 532 et 533. Quand il ne faut pas suivre : les chemins critiques de production qui exigent des API stables de façon permanente — attendez la version finale.

5. Mise en pratique

L’interrupteur. JDK 27 est déjà en GA (2026-09-15, build 35). JEP 534 est activée par défaut sans aucun paramètre : effective en LP64, désactivée en permanence en 32 bits ; vérifiez avec -XX:+PrintFlagsFinal que UseCompactObjectHeaders vaut bien true. JEP 532 et 533 sont en preview : il faut --enable-preview des deux côtés, compilation comme exécution (javac reçoit en plus --release 27) ; un seul côté manquant et vous êtes rejeté d’emblée.

Exécuter les exemples. Les exemples officiels des sections 2.2 et 2.3 constituent le point d’entrée minimal. Deux variantes à essayer : remplacez case int i par case long i pour observer l’erreur de dominance ; remplacez Joiner.anySuccessfulOrThrow() par le open() par défaut pour voir la différence dans la propagation des échecs. Essayer une fois par soi-même vaut mieux que lire dix fois les règles.

Lire le code source. Deux voies : le JDK installé embarque lib/src.zip, qu’il suffit de décompresser pour obtenir les sources de java.base et de jdk.compiler ; pour le C++ de HotSpot, passez par GitHub openjdk/jdk, faites un checkout du tag jdk-27+35, puis recopiez tels quels ces quatre fichiers : src/hotspot/share/oops/markWord.hpp (voir les commentaires L40–76 et les constantes L116–154), src/hotspot/share/runtime/globals.hpp (l’interrupteur en L131), src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java (L200–221 et L772–828), src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java (L373–1468). Méthode de lecture : commencez par les commentaires d’en-tête et la javadoc — les commentaires d’OpenJDK sont d’une densité exceptionnelle, schémas de disposition des bits et règles de transformation y sont écrits — puis sautez à l’implémentation avec vos questions en tête, sans vous acharner à lire ligne par ligne.

6. Comment construire soi-même une solution similaire

Pour le « reproduire soi-même » de cette rubrique appliqué au JDK, le chemin le plus réaliste consiste à passer de la lecture du code source à la soumission d’un petit patch à OpenJDK. OpenJDK est un projet gigantesque, mais ce n’est pas un mur infranchissable pour les contributeurs individuels ; décomposons cela en quatre étapes.

Étape 1, lire le code source pour poser les bases. On part des quatre fichiers de la section 5 et, après avoir bien assimilé les commentaires d’en-tête, on suit le fil : depuis markWord.hpp, on descend dans la hiérarchie oops ; depuis TransPatterns.java, on remonte vers TreeTranslator ainsi que vers Attr et Check du même répertoire ; StructuredTaskScope.java mène directement à la classe d’implémentation. L’objectif n’est pas de tout comprendre, mais de se forger l’intuition de « qui sera impacté quand on modifie un fichier ».

Étape 2, la construction locale. openjdk/jdk est livré avec son propre système de build ; sur les plateformes de type Unix, on lance ./configure puis make images. La première compilation complète prend une à deux heures ; ensuite, les compilations incrémentales ne durent que quelques dizaines de secondes. Savoir construire localement, c’est disposer de son banc d’essai.

Étape 3, exécuter les tests avant de modifier le code. OpenJDK utilise jtreg : les tests de javac se trouvent dans test/langtools, ceux de HotSpot dans test/hotspot/jtreg, et ceux des API du JDK dans test/jdk. La bonne méthode consiste à écrire d’abord de nouveaux cas de test qui échouent (les fonctionnalités d’aperçu apprécient tout particulièrement les cas limites supplémentaires), puis à modifier le code jusqu’à ce qu’ils passent.

Étape 4, le processus de soumission. Les étapes de bon sens : le style et le format des commits suivent le jcheck fourni avec le dépôt ; les modifications sont publiées sous forme de webrev sur la liste de revue du composant concerné et relues par un Committer ; avant une première contribution, il faut signer l’OCA (Oracle Contributor Agreement), par voie électronique, la page officielle des contributeurs faisant foi. Le choix du terrain : 532 et 533 sont encore en période d’aperçu, et les améliorations à faible risque comme la formulation de la javadoc, les messages d’erreur ou les tests de cas limites constituent une porte d’entrée réelle et appréciée ; la documentation du placement des bits et des options de HotSpot accepte elle aussi les petites retouches tout au long de l’année. Pour l’affectation précise et le système de mentorat, référez-vous aux indications en vigueur sur les pages de chaque projet.

Le tout, condensé en un schéma :

读源码(markWord.hpp / TransPatterns.java / StructuredTaskScope.java)
 → ./configure && make images(本地构建 JDK 27 镜像)
 → jtreg 跑目标组件测试(test/langtools、test/jdk……)
 → 先写失败用例,再改实现,再跑绿
 → jcheck → webrev 挂评审 → OCA(首次)→ Committer 合并

Répartition honnête de la difficulté : les deux premières étapes sont bouclées en deux semaines par une personne expérimentée ; à partir de la troisième, c’est la compréhension du composant qui est mise à l’épreuve ; la quatrième, avec ses allers-retours de revue, est la plus longue. Mais l’avantage est introuvable ailleurs : chaque ligne que vous modifiez tournera l’année prochaine sur des centaines de millions de JVM dans le monde entier.

Conclusion

Les trois fonctionnalités de JDK 27 sont les trois faces d’une même pièce : la JEP 534 comprime l’en-tête d’objet de 96 bits à 64 bits ; le schéma de bits de markWord.hpp et les commutateurs par défaut de globals.hpp prouvent qu’il s’agit d’un travail d’ingénierie mûr, les gains étant à évaluer selon les chiffres officiels ; la JEP 532 permet à instanceof et à switch de prendre en charge tous les types primitifs, et la traduction par rebaissement en deux phases de TransPatterns met en place la vérification d’exactness à l’exécution — cette cinquième préversion sans aucune modification signifie que la sémantique a convergé ; la JEP 533 intègre la concurrence structurée dans java.base, avec l’interface sealed, la machine à états à trois états et la stratégie par défaut awaitAllSuccessfulOrThrow évidentes à la lecture du code source. Aucune des trois n’introduit un nouveau paradigme : tout cela n’est que de la dette technique qui se rembourse. Le passage à la nouvelle version vous donne d’emblée la 534 ; pour les deux autres, activez --enable-preview pour vous familiariser, et attendez leur finalisation avant de les utiliser en production.

Sources de référence

  • JEP 534 : Compact Object Headers by Default (openjdk.org/jeps/534 ; Owner : Roman Kennke ; Closed/Delivered, Release 27)
  • JEP 450 : Compact Object Headers (Experimental) (openjdk.org/jeps/450 ; détails du bitmap de layout compact et du mécanisme de compression)
  • JEP 532 : Primitive Types in Patterns, instanceof, and switch (Fifth Preview) (openjdk.org/jeps/532 ; Owner : Angelos Bimpoudis ; système de règles et exemples officiels)
  • JEP 533 : Structured Concurrency (Seventh Preview) (openjdk.org/jeps/533 ; Authors : Alan Bateman, Viktor Klang, Ron Pressler ; forme de l’API, cinq changements et exemples officiels)
  • Code source d’openjdk/jdk (GitHub, tag jdk-27+35) : markWord.hpp (L40–76, L116–154), globals.hpp (L131–132, L150), TransPatterns.java (L200–221, L510, L772–828, L935), StructuredTaskScope.java (L373–1468)
  • Liste des JEP de la page du projet JDK 27 (openjdk.org/projects/jdk/27, « JDK 27 reached General Availability on 15 September 2026 »)
广告 · Advertisement

Questions fréquentes

Quelles sont les trois nouvelles fonctionnalités du JDK 27 ?

Le JDK 27 introduit trois nouvelles fonctionnalités : compression des en-têtes d'objets, pattern matching sur types primitifs et concurrence structurée.

Comment le JDK 27 compresse-t-il les en-têtes d'objets ?

Le JDK 27 compresse les en-têtes d'objets de 96 bits à 64 bits sur les architectures 64 bits, le ramenant de 96 bits à 64 bits.

Qu'est-ce que la concurrence structurée dans le JDK 27 ?

La concurrence structurée API java.base regroupe un ensemble de tâches de threads associées dans une portée refermable.