2013-03-02 11 views
5

द्वारा विरासत में प्राप्त विधियों के लिए स्थिरता नहीं रखता है मेरे पास निम्नलिखित कोड हैं।जावा बाइटकोड ऑपरेशन 'invokevirtual' ऑब्जेक्ट

public class Parent { 

    @Override 
    public int hashCode() { 
     return 0; 
    } 

} 

public class Child extends Parent { 

    public void test() { 
     this.toString(); 
     this.hashCode(); 
    } 

} 

आप ऊपर कोड में देख के रूप में, बाल toString() वस्तु और hashCode() जनक से से विरासत। चाइल्ड # टेस्ट का बाइटकोड ऑपरेशन निम्नानुसार है।

ALOAD 0: this 
INVOKEVIRTUAL Object.toString() : String 
ALOAD 0: this 
INVOKEVIRTUAL Child.hashCode() : int 
RETURN 

मैं अगर invokevirtual कॉल Object.toString लगता है(), यह स्थिरता के लिए Parent.hashCode() फोन करना चाहिए। या, Child.hashCode() कहा जाता है, तो Child.toString() को कॉल किया जाना चाहिए।

हालांकि, invokevirtual यदि और केवल यदि लक्ष्य विधि वस्तु द्वारा विरासत में मिली है इसकी निरंतरता नहीं रखता।

केवल उस मामले, वस्तु में invokevirtual कॉल विधि। अन्य मामलों के लिए, वर्तमान कक्षा में invokevirtual कॉल विधि।

मुझे पता है कि ऐसा क्यों होता चाहते हैं।

+5

यह देखते हुए कि वास्तविक विधि मार डाला * हमेशा * निष्पादन समय पर वास्तविक प्रकार के आधार पर किया जाएगा, क्यों इस बात करता है? वे समकक्ष हैं, निश्चित रूप से? –

+1

शायद संकलक ऑब्जेक्ट से विशेष-आवरण विरासत है, क्योंकि यह एक आम मामला है। –

+0

और यह आपके तर्क को कैसे प्रभावित करता है? – CuriousMind

उत्तर

4

आप सही है कि संकलक unlogical बर्ताव कर रहे हैं। लेकिन इस कोड का प्रभाव आपके द्वारा सुझाए गए दोनों प्रकारों के प्रभाव के समान है। तो यह संभवतः एक जानबूझकर व्यवहार नहीं है, लेकिन संकलक के कोड के लंबे विकास का परिणाम है। अन्य कंपाइलर अलग कोड उत्पन्न कर सकते हैं।

+0

आपका उत्तर मुख्य प्रश्न को शामिल नहीं करता है "ऐसा क्यों होता है?"। आपने केवल इतना कहा, कि: * हाँ, ऐसा होता है, क्योंकि ऐसा होता है *। – Andremoniy

+1

दरअसल, यह उत्तर सत्य के निकट आता है। अंतिम परिणाम में इसके पीछे कोई प्रेरणा नहीं हो सकती है; यह ऐसा ही होता है क्योंकि यह वास्तव में कोई फर्क नहीं पड़ता। –

+0

@ मार्को टॉपोलनिक मुझे पता है कि किसी भी मामले के लिए अंतिम परिणाम समान हो सकता है। हालांकि मुझे लगता है कि इस तरह से invocvirtual डिजाइन करने के कुछ कारण हो सकते हैं। ऑप्टिमाइज़ेशन के लिए, वे 'ऑब्जेक्ट .toString()' या 'parent.hashCode()' जैसे invokevirtual बना सकते हैं क्योंकि इस तरह, JVM इसे बिना खोज किए वास्तविक संदर्भ कार्यान्वयन को संदर्भित कर सकता है। हालांकि, उन्होंने नहीं किया। मुझे लगता है कि कुछ कारण हो सकते हैं। – Hozard

5

JVM specification p. 3.7 के अनुसार:

संकलक एक वर्ग उदाहरण के आंतरिक लेआउट नहीं जानता है। इसके बजाय, यह इंस्टेंस के तरीकों के प्रतीकात्मक संदर्भ उत्पन्न करता है, जो रनटाइम निरंतर पूल में संग्रहीत हैं। उन रनटाइम वास्तविक पूल आइटम वास्तविक विधि स्थान निर्धारित करने के लिए रनटाइम पर हल किए जाते हैं।

इसका मतलब है कि इन सभी प्रतीकात्मक Child.hashCode() केवल स्थिरांक है, जो कैसे JVM इस तरीकों कॉल किसी तरह का निर्दिष्ट नहीं कर रहे हैं। ऐसा लगता है, कि toString() विधि संकलक जानता है, इस विधि Object कक्षा में इसके आधार कार्यान्वयन है, तो यह लगातार पूल में Object वर्ग के लिए प्रतीकात्मक निरंतर डालता के लिए - इस अनुकूलन के लिए किसी तरह का है, जो JVM के लिए संकलक बनाता है:

Constant pool: 
const #2 = Method #24.#25; // java/lang/Object.toString:()Ljava/lang/String; 
... 
const #24 = class #34; // java/lang/Object 
const #25 = NameAndType #35:#36;// toString:()Ljava/lang/String; 
+0

क्या कंपाइलर 'हैशकोड' को समान उपचार नहीं देना चाहिए? यह इसके बारे में वही चीजें जानता है जैसे 'toString' और इसका प्रदर्शन, यदि कुछ भी हो, और भी महत्वपूर्ण है। –

+0

@ मार्कोटोपॉलिक 'हैशकोड' को 'माता-पिता' वर्ग में ओवरराइड किया गया था, इसलिए यह अनुकूलन इस मामले के लिए काम नहीं करता है। कंपाइलर JVM – Andremoniy

+0

संख्या के लिए इस विधि के संदर्भ समाधान को छोड़ देता है, विधि समाधान दोनों मामलों में बिल्कुल वही होगा। 'toString' को 'बच्चे' में इसे प्रतिबिंबित करने के लिए 'बाल' को पुन: संकलित किए बिना कार्यान्वयन मिल सकता है, और रनटाइम अभी भी ठीक से काम करना चाहिए। –

1

मेरे सिद्धांत: toString() बहुत बार प्रयोग किया जाता है, तो javac आम Object.toString() का उपयोग निरंतर पूल प्रविष्टियों को बचाने के लिए।

उदाहरण के लिए यदि कोड foo.toString() and bar.toString() होता है, contant पूल केवल एक Object.toString दो प्रविष्टियों Foo.toString and Bar.toString

javac शायद मुश्किल इस अनुकूलन कोडित के बजाय बजाय कोड का विश्लेषण देखने के लिए कि यह वास्तव में आवश्यक है की जरूरत है,,।

+0

तो यह 'हैशकोड' सहित 'ऑब्जेक्ट' वर्ग के सभी विधियों के लिए निरंतर पूल प्रविष्टियों को बचा सकता है। –

+0

हैशकोड() को अक्सर नहीं बुलाया जाता है। जबकि 'toString()' आमंत्रण हर वर्ग में लगभग मौजूद है। – irreputable

+0

यह बहुत ही भरोसेमंद लगता है, इस तरह की एक अच्छी परिकल्पना के लिए +1 :) हां, 'toString' कॉल साइट बनाम 'हैशकोड' कॉल साइट का अनुपात काफी व्यापक है। यह वास्तविक निरंतर पूल के आकलन द्वारा संचालित एक लक्षित अनुकूलन हो सकता था। 'toString' conflation को स्पष्ट जीत के रूप में पहचाना जा सकता था, जहां इसे 'हैशकोड' के लिए पहचाना नहीं गया था। इन शॉट्स को कॉल करने की स्थिति में बीटीडब्ल्यू 'जावैक' शायद ही कभी है; केवल जेवीएम का आकलन करने के लिए संपूर्ण वर्गपाथ है। –

संबंधित मुद्दे