web-dev-qa-db-fra.com

Comment la concaténation de chaînes est-elle implémentée dans Java 9?

Comme indiqué dans JEP 280: Indiquer la concaténation de chaînes :

Modifiez la séquence statique de bytecode String- de concaténation générée par javac pour utiliser les appels invokedynamic aux fonctions de la bibliothèque JDK. Cela permettra des optimisations futures de la concaténation String sans nécessiter de modifications supplémentaires du bytecode émis par javac.

Ici, je veux comprendre l’utilisation de invokedynamic appelle et comment la concaténation de bytecode est différente de invokedynamic?

107
Mohit Tyagi

La "vieille" manière de générer un tas d'opérations orientées StringBuilder. Considérez ce programme:

public class Example {
    public static void main(String[] args)
    {
        String result = args[0] + "-" + args[1] + "-" + args[2];
        System.out.println(result);
    }
}

Si nous compilons cela avec JDK version 8 ou antérieure, puis utilisons javap -c Example pour voir le bytecode, nous voyons quelque chose comme ceci:

 public class Exemple {
 public Exemple (); 
 Code: 
 0: aload_0 
 1: invokespecial # 1 // Méthode Java/lang /Object."<init>":()V
 4: retourne 
 
 Public static void main (Java.lang.String []); 
 Code : 
 0: nouveau # 2 // classe Java/lang/StringBuilder 
 3: dup 
 4: invokespecial # 3 // Méthode Java/lang/StringBuilder. "<Init> ":() V 
 7: aload_0 
 8: iconst_0 
 9: aaload 
 10: invokevirtual # 4 // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 13: ldc # 5 // String - 
 15: invokevirtual # 4 // Méthode Java/lang/StringBuilder.append :( Ljava/lang/String;) Ljava/lang/StringBuilder; 
 18: aload_0 
 19: iconst_1 
 20: aaload 
 2 1: invokevirtual # 4 // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 24: ldc # 5 // String - 
 26 : invokevirtual # 4 // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 29: aload_0 
 30: iconst_2 
. 31: aaload 
 32: invokevirtual # 4 // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 35: invokevirtual # 6 // Méthode Java/lang/StringBuilder.toString :() Ljava/lang/String; 
 38: astore_1 
 39: getstatic # 7 // Champ Java/lang/System.out: Ljava/io/PrintStream; 
 42: aload_1 
 43: invokevirtual # 8 // Méthode Java/io/PrintStream.println: (Ljava/lang/String;) V 
 46: retourne 
} 

Comme vous pouvez le constater, il crée un StringBuilder et utilise append. Ceci est assez célèbre car la capacité par défaut du tampon intégré dans StringBuilder n’est que de 16 caractères, et il n’existe aucun moyen pour le compilateur pour savoir affecter plus à l'avance, il faut donc réaffecter. C'est aussi un tas d'appels de méthodes. (Notez que la machine virtuelle Java peut parfois détecter et réécrire ces modèles d'appels pour les rendre plus efficaces, cependant.)

Regardons ce que Java 9 génère:

 public class Exemple {
 public Exemple (); 
 Code: 
 0: aload_0 
 1: invokespecial # 1 // Méthode Java/lang /Object."<init>":()V
 4: retourne 
 
 Public static void main (Java.lang.String []); 
 Code : 
 0: aload_0 
 1: iconst_0 
 2: aaload 
 3: aload_0 
 4: iconst_1 
 5: aaload 
 6: aload_0 
 7: iconst_2 
 8: aaload 
 9: invokedynamic # 2, 0 // InvokeDynamic # 0: makeConcatWithConstants: (Ljava/lang/String; Ljava/lang/String; Ljava/lang/String;) Ljava/lang/String; 
 14: astore_1 
 15: getstatic # 3 // Champ Java/lang/System.out: Ljava/io/PrintStream; 
 18: aload_1 
 19: invokevirtual # 4 // Méthode Java/io/PrintStream.println: (Ljava/lang/String;) V 
 22: retour 
} 

Oh mon mais c'est plus court. :-) Il fait un appel unique à makeConcatWithConstants de StringConcatFactory , ce qui est indiqué dans sa Javadoc:

Méthodes pour faciliter la création de méthodes de concaténation de chaînes, pouvant être utilisées pour concaténer efficacement un nombre connu d'arguments de types connus, éventuellement après adaptation de type et évaluation partielle des arguments. Ces méthodes sont généralement utilisées comme méthodes d'amorçage pour les sites d'appels invokedynamic, afin de prendre en charge la concaténation de chaînes . Fonction du Java Langage de programmation.

93
T.J. Crowder

Avant d'entrer dans les détails de l'implémentation invokedynamic utilisée pour l'optimisation de la concaténation de chaînes, à mon avis, il est nécessaire de connaître l'arrière-plan Qu'est-ce qui est appelé Invokedynamic et comment l'utiliser?

L'instruction invokedynamic simplifie et potentiellement améliore la mise en œuvre de compilateurs et de systèmes d'exécution pour les langages dynamiques sur la JVM. Pour ce faire, il permet à l’implémenteur de langage de définir un comportement de liaison personnalisé avec l’instruction invokedynamic qui implique les étapes suivantes.


J'essaierais probablement de vous expliquer toutes les modifications apportées pour la mise en œuvre de l'optimisation de la concaténation de chaînes.

  • Définition de la méthode Bootstrap : - avec Java9, les méthodes bootstrap pour invokedynamic sites d'appels, pour prendre en charge la concaténation de chaînes principalement makeConcat et makeConcatWithConstants ont été introduits avec le StringConcatFactory implémentation.

    L'utilisation de invokedynamic fournit une alternative pour sélectionner une stratégie de traduction jusqu'au moment de l'exécution. La stratégie de traduction utilisée dans StringConcatFactory est similaire à la méthode LambdaMetafactory introduite dans la previous Java version. En outre, l'un des objectifs du PEC mentionné dans la question est de développer davantage ces stratégies.

  • Spécification d'entrées de pool constantes : - Il s'agit des arguments statiques supplémentaires de l'instruction invokedynamic autres que (1) MethodHandles.Lookup objet qui est une fabrique pour la création de méthodes, dans le contexte de l’instruction invokedynamic, (2) un objet String, le nom de la méthode mentionné dans le site d’appel dynamique et ( 3) l'objet MethodType , la signature de type résolue du site d'appels dynamiques.

    Il y a déjà des liens lors du couplage du code. Au moment de l'exécution, la méthode bootstrap est exécutée et crée des liens dans le code lui-même effectuant la concaténation. Elle réécrit l'appel invokedynamic avec un invokestatic call. Ceci charge la chaîne constante du pool constant. Les arguments statiques de la méthode bootstrap sont exploités) pour transmettre ces constantes et d'autres constantes directement à la méthode bootstrap appeler.

  • Utilisation de l'instruction invokedynamic : - Ceci offre les facilités nécessaires pour une liaison paresseuse, en fournissant les moyens à bootstrap l'appel). cible une fois, lors de l'appel initial. L'idée d'optimisation concrète est de remplacer l'ensemble StringBuilder.append _ danse avec un simple invokedynamic appel à Java.lang.invoke.StringConcatFactory, qui acceptera les valeurs nécessitant une concaténation.

La proposition Indify String Concatenation indique, à l'aide d'un exemple, l'analyse comparative de l'application avec Java9, où une méthode similaire est partagée par @ T.J. Crowder est compilé et la différence dans le bytecode est assez visible entre les différentes implémentations.

20
Naman

Je vais ajouter un peu de détails ici. La partie principale à obtenir est que la concaténation de chaînes est effectuée par un décision d'exécution, pas de compilation. Cela peut donc changer, ce qui signifie que vous avez compilé votre code une fois contre Java-9 et que cela peut changer l'implémentation sous-jacente comme bon vous semble, sans qu'il soit nécessaire de recompiler.

Et le deuxième point est qu’à l’heure actuelle il y a 6 possible strategies for concatenation of String:

 private enum Strategy {
    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

Vous pouvez choisir n'importe lequel d'entre eux via un paramètre: -Djava.lang.invoke.stringConcat. Notez que StringBuilder est toujours une option.

18
Eugene