स्ट्रिंग कॉन्सेप्टेशन को जावा 9 में कैसे लागू किया गया है?


111

जेईपी 280 में लिखा गया है : स्ट्रींग कॉनटेनटेशन को इंडिफाई करें :

JDK लाइब्रेरी फ़ंक्शंस के लिए कॉल का उपयोग करने के लिए Stringजनरेट किए गए स्थैतिक- रूपांतरण बायटेकोड अनुक्रम को बदलें । यह भविष्य के अनुकूलन को सक्षम करेगा ताकि इसके द्वारा उत्सर्जित बाइटकोड को और अधिक परिवर्तन की आवश्यकता हो ।javacinvokedynamicStringjavac

यहां मैं यह समझना चाहता हूं कि invokedynamicकॉल का उपयोग क्या है और बाइटकोड से कैसे अलग है invokedynamic?


11
मैंने उसके बारे में कुछ समय पहले लिखा था - यदि वह मदद करता है, तो मैं उसे उत्तर में संक्षेपित करूंगा।
निकोलाई

10
इसके अलावा, इस वीडियो पर एक नज़र डालें जो अच्छी तरह से नए स्ट्रिंग
संघनन

3
@ZhekaKozlov मैं चाहता हूं कि मैं आपकी टिप्पणी को दो बार वोट कर सकता हूं, लिंक जो वास्तव में यह सब लागू करने वाले लोगों से आते हैं वे सबसे अच्छे हैं।
यूजीन

2
@ निकोलाई: यह बहुत अच्छा होगा, और यहाँ (मेरा सहित) किसी भी अन्य की तुलना में बेहतर उत्तर होगा। मेरे उत्तर के किसी भी हिस्से को आप जब भी शामिल करना चाहते हैं, स्वतंत्र महसूस करते हैं - यदि आप व्यापक रूप से उत्तर के हिस्से के रूप में पूरी (मूल रूप से) सम्मिलित करते हैं, तो मैं बस मेरा हटा दूंगा। वैकल्पिक रूप से, यदि आप केवल मेरे उत्तर को जोड़ना चाहते हैं क्योंकि यह काफी दृश्यमान है, तो मैंने इसे एक सामुदायिक विकि बना दिया है।
टीजे क्राउडर

जवाबों:


95

"पुराना" तरीका आउटपुट 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बाइटकोड देखने के लिए उपयोग करते हैं, तो हम कुछ इस तरह देखते हैं:

सार्वजनिक वर्ग उदाहरण {
  सार्वजनिक उदाहरण ();
    कोड:
       0: aload_0
       1: invokespecial # 1 // विधि जावा / लंग / वस्तु। "<init>" :() वी
       4: वापसी

  सार्वजनिक स्थैतिक शून्य मुख्य (java.lang.String []);
    कोड:
       0: नया # 2 // क्लास जावा / लैंग / स्ट्रिंगबर्ल
       3: डुबकी
       4: invokespecial # 3 // विधि java / lang / StringBuilder। "<init>" :( v)
       7: aload_0
       8: iconst_0
       9: अनलद
      10: invokevirtual # 4 // विधि java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
      13: ldc # 5 // स्ट्रिंग -
      15: invokevirtual # 4 // विधि java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
      18: aload_0
      19: iconst_1
      20: एलाड
      21: invokevirtual # 4 // विधि java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
      24: ldc # 5 // स्ट्रिंग -
      26: invokevirtual # 4 // विधि java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
      29: aload_0
      30: iconst_2
      31: एलाड
      32: invokevirtual # 4 // विधि java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
      35: invokevirtual # 6 // विधि java / lang / StringBuilder.toString :() Ljava / lang / स्ट्रिंग;
      38: astore_1
      39: getstatic # 7 // फ़ील्ड जावा / लैंग / सिस्टम.आउट: Ljava / io / PrintStream;
      42: aload_1
      43: invokevirtual # 8 // विधि जावा / io / PrintStream.println: (Ljava / lang / स्ट्रिंग;) वी
      46: वापसी
}

जैसा कि आप देख सकते हैं, यह एक बनाता है StringBuilderऔर उपयोग करता है append। यह बिल्‍ट-इन बफ़र की डिफ़ॉल्ट क्षमता के रूप में StringBuilderकेवल 16 वर्णों में डिफ़ॉल्ट रूप से अक्षम है , और संकलक के पास पहले से अधिक आवंटित करने के लिए जानने का कोई तरीका नहीं है , इसलिए यह वास्तविक रूप से समाप्त हो जाता है। यह विधि कॉल का एक गुच्छा भी है। (ध्यान दें कि JVM कभी-कभी कॉल के इन पैटर्नों का पता लगा सकता है और उन्हें फिर से लिख सकता है , हालांकि उन्हें अधिक कुशल बना सकता है।)

आइए देखें कि जावा 9 क्या उत्पन्न करता है:

सार्वजनिक वर्ग उदाहरण {
  सार्वजनिक उदाहरण ();
    कोड:
       0: aload_0
       1: invokespecial # 1 // विधि जावा / लंग / वस्तु। "<init>" :() वी
       4: वापसी

  सार्वजनिक स्थैतिक शून्य मुख्य (java.lang.String []);
    कोड:
       0: aload_0
       1: iconst_0
       2: एलाड
       3: aload_0
       4: iconst_1
       ५: अनलद
       6: aload_0
       7: iconst_2
       8: अनलद
       9: इनवॉक्लेनिस्टिक # 2, 0 // इनवोकेंडिऑनिक # 0: makeConcatWithConstants: (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String?) Ljava / lang / String;
      14: astore_1
      15: getstatic # 3 // फ़ील्ड जावा / लैंग / सिस्टम.आउट: Ljava / io / PrintStream;
      18: aload_1
      19: invokevirtual # 4 // विधि जावा / io / PrintStream.println: (Ljava / lang / स्ट्रिंग;) वी
      22: वापसी
}

ओह, लेकिन यह छोटा है। :-) यह एक एकल कॉल करता makeConcatWithConstantsहै StringConcatFactory, जो इसके जावदोक में कहता है:

स्ट्रिंग संघनन विधियों के निर्माण की सुविधा के लिए विधियाँ, जिनका उपयोग ज्ञात प्रकारों के तर्कों की एक संभावित संख्या को समझने के लिए किया जा सकता है, संभवतः प्रकार अनुकूलन और तर्कों के आंशिक मूल्यांकन के बाद। ये तरीके आमतौर पर कॉल साइटों के लिए बूटस्ट्रैप विधियों के रूप में उपयोग किए जाते हैं , जावा प्रोग्रामिंग लैंग्वेज invokedynamicके स्ट्रिंग कॉन्सेप्टन फीचर का समर्थन करने के लिए।


41
यह मुझे एक उत्तर की याद दिलाता है जो मैंने लगभग 6 साल पहले दिन तक लिखा था: stackoverflow.com/a/7586780/330057 - किसी ने पूछा कि क्या उन्हें एक स्ट्रिंगबर्ल बनाना चाहिए या सिर्फ +=लूप के लिए सादे पुराने का उपयोग करना चाहिए । मैंने उनसे कहा कि यह निर्भर करता है, लेकिन यह मत भूलो कि वे सड़क के नीचे कुछ समय के लिए स्ट्रिंग को बेहतर तरीके से ढूंढ सकते हैं। मुख्य पंक्ति वास्तव में So by being smart, you have caused a performance hit when Java got smarter than you.
प्रचलित

3
@ कोरसी: एलओएल! लेकिन वाह, वहाँ पहुंचने के लिए एक लंबा समय लगा (मुझे छह साल का मतलब नहीं है, मेरा मतलब है 22 या तो ... :-))
टीजे क्राउडर

1
@ सुपरकैट: जैसा कि मैं इसे समझता हूं, कुछ कारण हैं, कम से कम यह नहीं है कि एक प्रदर्शन-महत्वपूर्ण पथ पर एक विधि को पारित करने के लिए एक varargs सरणी बनाना आदर्श नहीं है। साथ ही, invokedynamicविभिन्न आहरण रणनीतियों को रनटाइम में चुने जाने और पहले आह्वान पर बाध्य करने की अनुमति देता है, बिना विधि आह्वान के उपरि के बिना और प्रत्येक आह्वान पर तालिका भेजना; यहाँ और JEP में निकोलई के लेख में अधिक ।
टीजे क्राउडर

1
@ सुपरकैट: और फिर यह तथ्य है कि यह गैर-स्ट्रिंग्स के साथ अच्छा नहीं खेलेगा, क्योंकि उन्हें अंतिम परिणाम में परिवर्तित होने के बजाय स्ट्रिंग में पूर्व-परिवर्तित करना होगा; अधिक अक्षमता। इसे बना सकते हैं Object, लेकिन फिर आपको सभी प्राइमेटिक्स को बॉक्स करना होगा ... (जो निकोलई अपने उत्कृष्ट लेख, बीटीडब्ल्यू में शामिल हैं।)
टीजे क्राउडर

2
@supercat I पहले से ही विद्यमान String.concat(String)विधि का उल्लेख कर रहा था जिसका क्रियान्वयन परिणामी स्ट्रिंग के सरणी को बना रहा है। फायदा तब हो जाता है जब हमें toString()मनमानी वस्तुओं पर आक्रमण करना होता है। इसी तरह, एरे को स्वीकार करने वाली विधि को कॉल करने पर, कॉल करने वाले को एरे को बनाना और भरना होता है जो समग्र लाभ को कम करता है। लेकिन अब, यह अप्रासंगिक है, क्योंकि नया समाधान मूल रूप से आप पर विचार कर रहे थे, सिवाय इसके कि इसमें कोई बॉक्सिंग ओवरहेड नहीं है, कोई सरणी निर्माण की आवश्यकता नहीं है, और बैकएंड विशेष परिदृश्यों के लिए अनुकूलित हैंडलर उत्पन्न कर सकता है।
होल्गर

20

invokedynamicस्ट्रिंग कंक्रीटिंग के अनुकूलन के लिए उपयोग किए जाने वाले कार्यान्वयन के विवरण में जाने से पहले , मेरी राय में, किसी को व्हाट्सएप डायनामिक पर कुछ पृष्ठभूमि प्राप्त करनी चाहिए और मैं इसका उपयोग कैसे करूं?

invokedynamic अनुदेश सरल और संभावित संकलनकर्ता और JVM पर गतिशील भाषाओं के लिए क्रम प्रणालियों के कार्यान्वयन को बेहतर बनाता है । यह भाषा कार्यान्वयनकर्ता को invokedynamicनिर्देश के साथ कस्टम लिंकेज व्यवहार को परिभाषित करने की अनुमति देता है जिसमें निम्न चरणों का समावेश होता है।


मैं संभवतः इन परिवर्तनों के साथ आपके माध्यम से प्रयास करूंगा जो स्ट्रिंग संगति अनुकूलन के कार्यान्वयन के लिए लाया गया था।

  • बूटस्ट्रैप विधि को परिभाषित करना : - जावा 9 के साथ, invokedynamicकॉल साइटों के लिए बूटस्ट्रैप विधियाँ , मुख्य रूप से स्ट्रिंग संघनन का समर्थन करने के लिए makeConcatऔर कार्यान्वयन के makeConcatWithConstantsसाथ पेश की गई थीं StringConcatFactory

    चालित काल तक अनुवाद रणनीति का चयन करने के लिए इनवॉक्लेनेमिक का उपयोग एक विकल्प प्रदान करता है। में उपयोग की जाने वाली अनुवाद की रणनीति पिछले जावा संस्करण में प्रस्तुत StringConcatFactoryकी LambdaMetafactoryगई के समान है । इसके अतिरिक्त प्रश्न में उल्लेखित JEP का एक लक्ष्य इन रणनीतियों को और आगे बढ़ाना है।

  • निरंतर ताल प्रविष्टियाँ निर्दिष्ट करना : - ये invokedynamic(1) MethodHandles.Lookupवस्तु के अलावा अन्य अनुदेश के अतिरिक्त स्थिर तर्क हैं, जो निर्देश के संदर्भ में विधि हैंडल बनाने का कारखाना है invokedynamic, (2) एक Stringवस्तु, डायनेमिक कॉल में उल्लिखित विधि नाम साइट और (3) MethodTypeऑब्जेक्ट, डायनेमिक कॉल साइट के हल किए गए प्रकार के हस्ताक्षर।

    कोड के लिंक के दौरान पहले से ही लिंक किए गए हैं। रनटाइम के दौरान, बूटस्ट्रैप विधि चलती है और वास्तविक कोड में लिंक करती है। यह invokedynamicएक उपयुक्त invokestaticकॉल के साथ कॉल को फिर से लिखता है । यह निरंतर पूल से निरंतर स्ट्रिंग को लोड करता है, बूटस्ट्रैप विधि स्थिर आर्ग को इन और अन्य स्थिरांक को सीधे बूटस्ट्रैप विधि कॉल में पास करने के लिए लीवरेज किया जाता है।

  • इनवॉकेन्डेस्टिक इंस्ट्रक्शन का उपयोग करना : - यह एक प्रारंभिक लिंक के दौरान एक बार कॉल लक्ष्य को बूटस्ट्रैप करने के साधन प्रदान करके, एक आलसी लिंकेज के लिए सुविधाएं प्रदान करता है। यहां अनुकूलन के लिए ठोस विचार पूरे StringBuilder.appendनृत्य को एक साधारण invokedynamicकॉल के साथ बदलना है java.lang.invoke.StringConcatFactory, जो सहमति की आवश्यकता के मूल्यों को स्वीकार करेगा।

इंडिफाई स्ट्रिंग संयोजन एक उदाहरण के साथ प्रस्ताव राज्यों के रूप में द्वारा साझा एक समान विधि Java9 साथ आवेदन की बेंच मार्किंग @TJ Crowder संकलित किया गया है और बाईटकोड में अंतर काफी परिवर्तनीय कार्यान्वयन के बीच दिख रहा है।


17

मैं यहाँ थोड़ा विवरण जोड़ूंगा। प्राप्त करने के लिए मुख्य भाग यह है कि स्ट्रिंग समवर्ती कैसे किया जाता है यह एक रनटाइम निर्णय है, न कि एक संकलन समय । इस प्रकार यह बदल सकता है, जिसका अर्थ है कि आपने जावा -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अभी भी एक विकल्प है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.