आप एक ही के लगातार फांसी के बीच एक अंतर बनाने के लिए है कॉल-साइट , राज्यविहीन लैम्ब्डा या स्टेटफुल lambdas, और एक के लगातार उपयोग के लिए विधि-संदर्भ एक ही विधि के लिए (अलग कॉल-साइटों द्वारा)।
निम्नलिखित उदाहरण देखें:
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=System::gc;
if(r1==null) r1=r2;
else System.out.println(r1==r2? "shared": "unshared");
}
यहां, एक ही कॉल-साइट को दो बार निष्पादित किया जाता है, एक स्टेटलेस लैम्ब्डा का उत्पादन होता है और वर्तमान कार्यान्वयन प्रिंट होगा "shared"
।
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=Runtime.getRuntime()::gc;
if(r1==null) r1=r2;
else {
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
}
}
इस दूसरे उदाहरण में, एक ही कॉल-साइट को दो बार निष्पादित किया जाता है, एक लंबो का निर्माण करता है जिसमें एक Runtime
उदाहरण के संदर्भ में और वर्तमान कार्यान्वयन प्रिंट होगा "unshared"
लेकिन "shared class"
।
Runnable r1=System::gc, r2=System::gc;
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
इसके विपरीत, अंतिम उदाहरण में दो अलग-अलग कॉल-साइट एक समान विधि संदर्भ का उत्पादन कर रहे हैं लेकिन जैसा कि 1.8.0_05
यह प्रिंट करेगा "unshared"
और "unshared class"
।
प्रत्येक लैम्ब्डा अभिव्यक्ति या विधि संदर्भ के लिए कंपाइलर एक invokedynamic
निर्देश का उत्सर्जन करेगा जो कि क्लास में JRE प्रदान की गई बूटस्ट्रैप विधि LambdaMetafactory
और वांछित लैम्ब्डा कार्यान्वयन वर्ग के उत्पादन के लिए आवश्यक स्थिर तर्क को संदर्भित करता है । यह वास्तविक JRE के लिए छोड़ दिया जाता है कि मेटा फैक्ट्री क्या पैदा करती है, लेकिन यह पहले आह्वान पर बनाए invokedynamic
गए CallSite
उदाहरण को याद रखने और फिर से उपयोग करने के लिए निर्देश का एक निर्दिष्ट व्यवहार है ।
वर्तमान जेआरई स्टेटलेस लैम्ब्डा के लिए एक स्थिर ऑब्जेक्ट से ConstantCallSite
युक्त है MethodHandle
(और इसे अलग करने के लिए कोई कल्पना करने योग्य कारण नहीं है)। और विधि के लिए विधि संदर्भ static
हमेशा स्टेटलेस होते हैं। इसलिए स्टेटलेस लैम्ब्डा और सिंगल कॉल-साइट्स के लिए इसका उत्तर होना चाहिए: कैश न करें, जेवीएम करेगा और यदि ऐसा नहीं है, तो इसके मजबूत कारण होने चाहिए कि आपको जवाबी कार्रवाई नहीं करनी चाहिए।
लैम्ब्डा के मापदंडों के लिए, और this::func
एक लैम्ब्डा है जिसमें this
उदाहरण के लिए एक संदर्भ है , चीजें थोड़ी अलग हैं। JRE को उन्हें कैश करने की अनुमति है, लेकिन यह Map
वास्तविक पैरामीटर मानों और परिणामी लैम्ब्डा के बीच कुछ प्रकार को बनाए रखेगा, जो कि उस सरल संरचित लैम्बडा उदाहरण को फिर से बनाने की तुलना में अधिक महंगा हो सकता है। वर्तमान जेआरई राज्य में लंबा उदाहरणों को कैश नहीं करता है।
लेकिन इसका मतलब यह नहीं है कि मेमना वर्ग हर बार बनाया जाता है। इसका सीधा सा मतलब यह है कि सुलझी हुई कॉल-साइट एक सामान्य वस्तु निर्माण की तरह व्यवहार करेगी, जो लैम्ब्डा क्लास को इंस्टेंट करने वाली है जो पहले आह्वान पर उत्पन्न हुई है।
अलग-अलग कॉल-साइट्स द्वारा बनाई गई एक ही लक्ष्य पद्धति के संदर्भ में समान बातें लागू होती हैं। JRE को उनके बीच एक एकल लंबो इंस्टेंस को साझा करने की अनुमति है, लेकिन वर्तमान संस्करण में यह संभव नहीं है, क्योंकि यह स्पष्ट नहीं है कि कैश रखरखाव का भुगतान करना होगा या नहीं। यहां, उत्पन्न वर्ग भी भिन्न हो सकते हैं।
इसलिए आपके उदाहरण की तरह कैशिंग करने से आपका कार्यक्रम बिना किसी से अलग हो सकता है। लेकिन जरूरी नहीं कि वह अधिक कुशल हो। एक कैश्ड वस्तु हमेशा एक अस्थायी वस्तु की तुलना में अधिक कुशल नहीं होती है। जब तक आप वास्तव में एक लैम्ब्डा निर्माण के कारण प्रदर्शन प्रभाव को मापते हैं, आपको कोई कैशिंग नहीं जोड़ना चाहिए।
मुझे लगता है, केवल कुछ विशेष मामले हैं जहां कैशिंग उपयोगी हो सकता है:
- हम कई अलग-अलग कॉल-साइटों के बारे में बात कर रहे हैं जो एक ही विधि का उल्लेख करते हैं
- कंस्ट्रक्टर / क्लास में लैम्ब्डा बनाया जाता है, क्योंकि बाद में उपयोग-साइट वसीयत में होता है
- कई सूत्र द्वारा समवर्ती रूप से बुलाया जा सकता है
- पहले आह्वान के निचले प्रदर्शन से पीड़ित हैं