2010-07-29 4 views
6

मैं जावा finalizers के बारे में these slides पढ रहा हूं। इस रिपोर्ट में लेखक (स्लाइड 33 पर) एक परिदृश्य जिससे CleanResource.finalize() जबकि CleanResource.doSomething() अभी भी एक और धागा पर चल रहा है finalizer धागा द्वारा चलाए जा सकता है वर्णन करता है। यह कैसे हो सकता है?जावा जीसी प्रश्न: कैसे एक वस्तु नहीं पहुंचा जा सकता हो सकता है, जबकि इसकी विधियों में से एक अभी भी निष्पादित किया जा रहा है?

यदि doSomething() एक गैर स्थैतिक विधि है, तो उस विधि को निष्पादित करने के लिए, कहीं का एक मजबूत संदर्भ होना चाहिए ... सही? तो विधि रिटर्न से पहले यह संदर्भ कैसे निकाला जा सकता है? क्या कोई और धागा उस संदर्भ में घुसपैठ कर सकता है? अगर ऐसा हुआ, doSomething() अभी भी मूल धागे पर सामान्य रूप से वापसी होगी?

सब मैं सच में जानना चाहते है कि है, लेकिन एक वास्तव में के लिए ऊपर और परे जवाब है, क्या आप मुझे बता सकते हैं क्यों स्लाइड 38 पर doSomething() स्लाइड 29 doSomething() से बेहतर है क्यों यह बस के लिए पर्याप्त है इस keepAlive() विधि का आह्वान करें? यदि आप एक synchronized(this){} ब्लॉक में myImpl.doSomething() करने के लिए पूरे कॉल लपेटो करने की जरूरत नहीं होगी?

उत्तर

3

EDIT3:

नतीजा यह है कि finalizer और एक नियमित विधि एक ही उदाहरण पर समवर्ती क्रियान्वित किया जा सकता है। यहां बताया गया है कि यह कैसे हो सकता है। कोड अनिवार्य है:

class CleanResource { 
    int myIndex; 
    static ArrayList<ResourceImpl> all; 

    void doSomething() { 
    ResourceImpl impl = all.get(myIndex); 
    impl.doSomething(); 
    } 

    protected void finalize() { ... } 
} 

इस ग्राहक कोड को देखते हुए:

CleanResource resource = new CleanResource(...); 
resource.doSomething(); 
resource = null; 

यह इस छद्म सी

register CleanResource* res = ...; call ctor etc.. 
// inline CleanResource.doSomething() 
register int myIndex = res->MyIndex; 
ResourceImpl* impl = all->get(myInddex); 
impl->DoSomething(); 
// end of inline CleanResource.doSomething() 
res = null; 

ऐसे ही निष्पादित की तरह कुछ करने के लिए JITed हो सकता है, res के बाद मंजूरी दे दी है inlined CleanResource.doSomething(), किया जाता है ताकि जीसी जब तक के बाद कि विधि का निष्पादन पूरा होने नहीं होगा। एक ही उदाहरण पर एक और उदाहरण विधि के साथ एक साथ निष्पादन को अंतिम रूप देने की कोई संभावना नहीं है।

लेकिन, res को लिखने उस बिंदु के बाद नहीं किया जाता है, और यह देखते हुए कि वहाँ कोई बाड़ हैं कि, यह निष्पादन में पहले ले जाया जा सकता लिखने के बाद करने के लिए तुरंत:

register CleanResource* res = ...; call ctor etc.. 
// inline CleanResource->doSomething() 
register int myIndex = res->MyIndex; 
res = null; /// <----- 
ResourceImpl* impl = all->get(myInddex); 
impl.DoSomething(); 
// end of inline CleanResource.doSomething() 

चिह्नित पर स्थान (< ---), CleanResource उदाहरण के लिए कोई संदर्भ नहीं हैं, और इसलिए यह संग्रह और अंतिमकरण विधि के लिए योग्य है। चूंकि अंतिम संदर्भ को अंतिम संदर्भ के बाद अंतिम बार बुलाया जा सकता है, इसलिए अंतिम रूप में निष्पादित करने के लिए अंतिमकरण और CleanResource.doSomething() के शेष के लिए यह संभव है।

EDIT2: KeepAlive() सुनिश्चित करता है कि this सूचक, विधि के अंत में पहुँचा जा सकता है ताकि संकलक सूचक के उपयोग दूर अनुकूलित नहीं कर सकते। और वह इस पहुँच निर्दिष्ट क्रम में होने की गारंटी है (सिंक्रनाइज़ शब्द एक बाड़ कि फिर से आदेश देने की अनुमति नहीं देता है पढ़ता है और उस बिंदु के बाद/पहले लिखते हैं की चिह्नित करता है।)

मूल पोस्ट:

उदाहरण कह रहा है कि doSomething विधि को बुलाया जाता है, और एक बार बुलाया जाता है, this पॉइंटर के माध्यम से संदर्भित डेटा को प्रारंभ में पढ़ा जा सकता है (उदाहरण में myIndex)।एक बार संदर्भित डेटा पढ़े जाने के बाद, उस विधि में this पॉइंटर की आवश्यकता नहीं है, और सीपीयू/कंपाइलर रजिस्टरों को ओवरराइट कर सकता है/वस्तु को घोषित नहीं कर सकता है। इसलिए, जीसी तब एक साथ समय पर अंतिमकर्ता को कॉल कर सकता है क्योंकि ऑब्जेक्ट की कुछ() विधि चल रही है।

लेकिन चूंकि this सूचक नहीं किया जाता है, यह देखने के लिए कि यह कैसे किसी भी ठोस असर नहीं होगा मुश्किल है।

संपादित करें: ठीक है, शायद अगर कैश के माध्यम से एक्सेस किए जा रहे ऑब्जेक्ट के फ़ील्ड में कैश किए गए पॉइंटर्स हैं, तो इसे पुनः प्राप्त करने से पहले this से गणना की गई है, और ऑब्जेक्ट को फिर से याद किया गया है कि स्मृति संदर्भ अमान्य हो गए हैं। मेरे एक ऐसा हिस्सा है जिस पर विश्वास करना कठिन समय है, लेकिन फिर, यह एक मुश्किल कोने का मामला प्रतीत होता है, और मुझे नहीं लगता कि जेएसआर -133 में कुछ भी डिफ़ॉल्ट रूप से होने से रोकने के लिए कुछ भी है। यह एक प्रश्न है कि किसी ऑब्जेक्ट को केवल पॉइंटर्स द्वारा इसके आधार पर या पॉइंटर्स द्वारा इसके फ़ील्ड में संदर्भित किया जाता है या नहीं।

+0

तो क्या यह एक स्टैक-आधारित वीएम पर भी हो सकता है? अब जब मैं बाइटकोड स्तर पर सोच रहा हूं तो आप इसे बेहतर समझ रहे हैं। मुझे लगता है कि एक स्टैक मशीन के मामले में संदर्भ को 'invokevirtual' को पूरा करने के लिए स्टैक पर होने की आवश्यकता नहीं है? तो एक बार 'myIndex' मान पुनर्प्राप्त हो जाने के बाद' यह 'ढेर से पॉप किया जा सकता है और संभावित रूप से पुनः दावा किया जा सकता है? –

+0

सख्त स्टैक-आधारित कार्यान्वयन पर, यह संभव नहीं है, क्योंकि यह सूचक ढेर पर रहेगा। लेकिन विधि निष्पादन के साथ, आदेश निष्पादन से बाहर और आवंटन रजिस्टर, अंतिमकर्ता को समवर्ती कॉल संभव हो जाता है। इस निरंतर गाथा में मेरा नवीनतम संपादन देखें। :) – mdma

+0

महान जवाब! मैं एंड्रॉइड के दलविक वीएम के अलावा किसी अन्य को भी नहीं जानता था, जो एक पंजीकृत-आधारित जेवीएम लागू किया था। क्या यह आम है? –

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