كيف يتم تطبيق تسلسل السلسلة في Java 9؟




string string-concatenation (2)

كما هو مكتوب في JEP 280: حدد سلسلة السلسلة :

قم بتغيير تسلسل كود ثابت String ثابت الذي تم إنشاؤه بواسطة javac لاستخدام المكالمات invokedynamic إلى وظائف مكتبة JDK. سيؤدي ذلك إلى تمكين عمليات تحسين تسلسل String المستقبل دون الحاجة إلى مزيد من التغييرات على الرمز الثانوي الذي تم إصداره بواسطة javac .

هنا أريد أن أفهم ما هو استخدام المكالمات invokedynamic وكيف يختلف تسلسل bytecode عن invokedynamic ؟


إخراج الطريقة "القديمة" مجموعة من عمليات توجيه StringBuilder . النظر في هذا البرنامج:

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

إذا قمنا بترجمة ذلك باستخدام JDK 8 أو إصدار سابق ، ثم استخدم javap -c Example لرؤية javap -c Example ، سنرى شيئًا مثل هذا:

public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: aload_0
       8: iconst_0
       9: aaload
      10: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      13: ldc           #5                  // String -
      15: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: aload_0
      19: iconst_1
      20: aaload
      21: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: ldc           #5                  // String -
      26: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: aload_0
      30: iconst_2
      31: aaload
      32: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      38: astore_1
      39: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: aload_1
      43: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      46: return
}

كما ترون ، فإنه ينشئ StringBuilder ويستخدم append . هذا مشهور غير فعال إلى حد ما لأن السعة الافتراضية للمخزن المؤقت المدمج في StringBuilder هي 16 حرفًا فقط ، ولا توجد طريقة لمعرفة المحول البرمجي لتخصيص المزيد مسبقًا ، لذلك ينتهي الأمر إلى إعادة تخصيص. إنها أيضًا مجموعة من مكالمات الطريقة. (لاحظ أن JVM يمكنها في بعض الأحيان اكتشاف أنماط المكالمات هذه وإعادة كتابتها لجعلها أكثر فاعلية ، على الرغم من ذلك.)

دعنا ننظر إلى ما يولد جافا 9:

public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  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                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: aload_1
      19: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: return
}

يا بلدي ولكن هذا أقصر. :-) إنها تجري مكالمة واحدة إلى makeConcatWithConstants من StringConcatFactory ، والتي تقول هذا في Javadoc:

طرق لتسهيل إنشاء طرق تسلسل السلسلة ، والتي يمكن استخدامها لتسلسل عدد معروف من الوسائط من الأنواع المعروفة بكفاءة ، ربما بعد تكييف الكتابة والتقييم الجزئي للوسائط. تستخدم هذه الطرق عادةً invokedynamic لمواقع استدعاء invokedynamic ، لدعم ميزة تسلسل السلسلة في لغة برمجة Java.


سأضيف قليلا من التفاصيل هنا. الجزء الرئيسي الذي يجب الحصول عليه هو أن كيفية عمل تسلسل السلسلة هو قرار وقت التشغيل ، وليس وقت تجميع بعد الآن . وبالتالي يمكن أن يتغير ، وهذا يعني أنك قمت بترجمة الشفرة مرة واحدة مقابل java-9 ، ويمكن أن تغير التطبيق الأساسي بغض النظر عن ما تريد ، دون الحاجة إلى إعادة الترجمة.

والنقطة الثانية هي أنه في الوقت الحالي هناك 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
}

يمكنك اختيار أي منهم عبر معلمة: -Djava.lang.invoke.stringConcat . لاحظ أن StringBuilder لا يزال خيارًا.





invokedynamic