2015-09-04 6 views
7

मैं लगातार स्मृति रिसाव की पहचान के साथ संघर्ष कर रहा हूं। मुझे लगता है कि मेरे प्रोजेक्ट circular progress view में कई मेमोरी लीक हैं।व्यावहारिक उदाहरण में मेमोरी लीक

मेरे अनुमानों में से एक है कि मेरे पास आंतरिक कक्षा FadeRunnable में मेमोरी लीक है। लेकिन ईमानदार होने के लिए मुझे नहीं पता कि यह कैसे पता लगाना है कि यह वास्तव में समस्या का स्रोत है या नहीं। खैर, जब मैं सामान्य परिदृश्य करता हूं और अभिविन्यास स्विच करता हूं तो मुझे नीचे दिखाए गए स्मृति उपयोग में वृद्धि दिखाई देती है। और अगर मैं FadeRunnable वर्ग के उपयोग बाहर टिप्पणी चरणों में छोटे होते हैं (लेकिन अभी भी वहाँ है, इसलिए मुझे लगता है कि यह है कि केवल रिसाव नहीं है)

memory steps

एक बार मैं हीप डंप का विश्लेषण, मैं कुछ देख । लेकिन वास्तव में मुझे नहीं पता कि मूल्यों का क्या अर्थ है। बातें मुझे क्या करना

  • ओपन हीप डंप और तरह से 'बनाए रखा आकार'
  • अब जब मैं "CircularProgressView 'मैं सही क्षेत्र में 8 पंक्तियां देखने पर क्लिक करें कई बार

    1. उन्मुखीकरण बदलें है। मुझे लगता है कि इसका मतलब यह है की 'CircularProgressView' लीक और कहीं स्मृति में अनाथ के रूप में रहने वाले।

    यह सही? मैं कहीं लगता है कि यदि हां, तो मैं कैसे डंप जानकारी में पता कर सकते हैं (है 8 उदाहरणों देखते हैं निचला फलक) जहां यह वस्तु सहेजी/आयोजित की जाती है।

    heap dump

    मैं एक कदम-दर-कदम विवरण पता लगाने के लिए अगर और जो वस्तु कुछ स्मृति लीक कर रहा है अच्छा लगेगा।

    संदिग्ध दृश्य के सभी कोड इस कक्षा में पाए जा सकते हैं।

    https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java

    लेकिन यह भी गहन जानकारी के लिए पूरी परियोजना की जाँच करने के लिए स्वतंत्र लग रहा है और आप इसके साथ चारों ओर खेलने के लिए चाहते हैं।

    अग्रिम धन्यवाद!

    अद्यतन

    ऊपर से कोड लिंक मेम-लीक भीतरी वर्ग की नियत कोड को दर्शाता है। निम्नलिखित स्निपेट मूल मेम-लीक कोड है कि

    /** 
    * Mem-leaking code, for fixed code see repository link 
    * https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java 
    */ 
    public class CircularProgressView extends View 
    { 
        ... 
        private Thread fadeThread = null; 
        ... 
    
        ... 
        class FadeRunnable implements Runnable 
        { 
         @Override 
         public void run() 
         { 
          ... 
         } 
        } 
        ... 
    
        ... 
        private void startFade(boolean fadeIn) 
        { 
         // check existing 
         if(this.fadeThread != null) 
         { 
          // check if fade is already running 
          switch(this.fadeThread.getState()) 
          { 
           case TERMINATED: 
           case NEW: 
            this.fadeThread = null; 
            break; 
           case RUNNABLE: 
           case BLOCKED: 
           case TIMED_WAITING: 
           case WAITING: 
            return; 
          } 
         } 
         // create new 
         this.fadeThread = new Thread(new FadeRunnable(fadeIn, this.fadeTime)); 
         this.fadeThread.start(); 
        } 
    } 
    
  • +1

    मैं लीक कैनरी लाइब्रेरी से शुरू करने की अनुशंसा करता हूं: https://github.com/square/leakcanary। यदि आप संदर्भ रिसाव करते हैं तो बॉक्स को एकीकृत और बाहर करना बेहद आसान है। –

    +0

    हां, इसे पहले से ही मेरी बड़ी परियोजनाओं के लिए उपयोग करें। लेकिन वास्तव में मुझे तीसरे पक्ष के lib पर भरोसा करने की बजाय पूरी चीज को समझने में दिलचस्पी है! –

    उत्तर

    5

    हां, आपके पास FadeRunnable कक्षा में मेमोरी लीक है।

    आंतरिक कक्षा के प्रत्येक उदाहरण में OuterClass.this ऑपरेटर के माध्यम से सुलभ, बाहरी कक्षा के निहित संदर्भ शामिल हैं। अपनी प्रोजेक्ट में, जब आप FadeRunnable निष्पादित करते हैं और उसके बाद अभिविन्यास परिवर्तन, संपूर्ण गतिविधि और CircularProgressView पुन: निर्मित होने के भीतर निहित ट्रिगर को ट्रिगर करते हैं, लेकिन पिछले से FadeRunnable अभी भी जिंदा है (आवंटित) और, क्योंकि इसके बाहरी संदर्भ में अंतर्निहित संदर्भ है CircularProgressView वर्ग, दृश्य भी जारी रहता है, यही कारण है कि कई पुनर्गठन के बाद आपके पास स्मृति में आवंटित CircularProgressView के 8 उदाहरण हैं, और यह बदतर हो जाता है - प्रत्येक दृश्य इसके संदर्भ का संदर्भ रखता है, और इसे भी मुक्त नहीं किया जा सकता है, जिसके परिणामस्वरूप खराब स्मृति लीक।

    रननेबल, हैंडलर और इसी तरह की ऑब्जेक्ट्स जो अपनी संलग्न गतिविधियों, टुकड़े, विचार इत्यादि से बाहर रह सकती हैं उन्हें मानक कक्षाएं या स्टेटिक आंतरिक कक्षाओं के रूप में घोषित किया जाना चाहिए (एक स्थिर आंतरिक वर्ग को बाहरी कक्षा में अंतर्निहित संदर्भ नहीं है) , और Context, View इत्यादि जैसे संदर्भों को नहीं रखना चाहिए, इसके बजाय आप WeakReference<> रख सकते हैं, इसलिए जब आपका Activity कॉन्फ़िगरेशन परिवर्तन के माध्यम से पुनर्निर्मित किया जाता है, तो View को कचरा कलेक्टर द्वारा नष्ट और मुक्त किया जा सकता है।

    This विषय पर एक बहुत ही जानकारीपूर्ण लेख है, मैं दृढ़ता से इसे पढ़ने का सुझाव देता हूं।

    +0

    बहुत बहुत धन्यवाद। मुझे वह परिदृश्य मिला। लेकिन क्या आपके पास सामान्य चरण-दर-चरण स्मृति रिसाव विश्लेषण के लिए कुछ लिंक है? मुझे यह पता लगाने के लिए ढेर डंप का उपयोग करने में रूचि है कि 'फेडरुनेबल' कारण है। –

    +0

    नहीं कि मैं अभी इस बारे में सोच सकता हूं, क्षमा करें – maciekjanusz

    2

    मुझे लगता है कि तुम वहाँ सही दिशा है की तरह प्रयोग नहीं करना चाहिए पता चलता है। यह FadeRunnable निश्चित रूप से अच्छा नहीं है। यहां तक ​​कि यदि आपके पास अन्य मेमोरी लीक हैं, तो आप इसे जांचना चाहिए।

    सामान्य रूप से आपको वास्तव में एक दृश्य में क्या करना चाहिए, यह काफी अलग है, विशेष रूप से विचारों में पहले से ही थ्रेड की आवश्यकता के बिना समय और एनीमेशन से निपटने की सुविधा है।

    मैं आपको सुझाव दूंगा कि मुझे विश्वास है कि विचारों पर चीजों को एनिमेट करने के लिए एक सरल और साफ दृष्टिकोण है।

    • अपने रननेबल और थ्रेड को पूरी तरह से हटाकर प्रारंभ करें।

    तो एक एनीमेशन तुम क्या शुरू करने के लिए:

    ValueAnimator animation = ValueAnimator.ofFloat(0, 1); 
    animation.setDuration(500); 
    animation.addUpdateListener(animationUpdate); 
    animation.addListener(animationUpdate); 
    animation.start(); 
    

    और उसके बाद आप उन श्रोताओं

    // this gets called for every animation update, 
        // inside this call you update `CircularProgressView.this.fadeAlpha` 
        private final ValueAnimator.AnimatorUpdateListener animationUpdate = new ValueAnimator.AnimatorUpdateListener() { 
         @Override public void onAnimationUpdate(ValueAnimator animation) { 
         // this fraction varies between 0f and 1f 
         float fraction = animation.getAnimatedFraction(); 
         // ... do your calculation 
    
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
         } 
        }; 
    
        // this is an optional one only if you really need 
        // in that you get notified when the animation starts and ends 
        private final Animator.AnimatorListener animationListener = new AnimatorListenerAdapter() { 
    
        @Override public void onAnimationStart(Animator animation) { 
         // anything u need goes here 
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
        } 
    
         @Override public void onAnimationEnd(Animator animation) { 
         // anything u need goes here 
         ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); 
         } 
        }; 
    

    और कहा कि इसके बारे में की जरूरत है।

    वास्तविक मेमोरी रिसाव विश्लेषण के विषय पर मैं आपको अब से हमेशा के लिए रिसाव कैनरी लाइब्रेरी का उपयोग करने का सुझाव दूंगा: https://github.com/square/leakcanary यह स्मृति (डेवलपर्स) मेमोरी लीक ट्रैक करने में हमारी सहायता करने के लिए एक शानदार टूल है।

    संपादित करें:

    आप इस एनीमेशन पर एक स्मृति रिसाव क्यों हो रही है? यह काफी सरल है:

    • startFade(boolean); पर आप एक नया धागा और एक नया runnable एक
    • runnable दृश्य के लिए एक संदर्भ
    • धागा (क्योंकि यह एक गैर स्थिर भीतरी वर्ग है) किया है बनाने Runnable के संदर्भ में, तो इसे चला सकते हैं।
    • ढांचे, दृश्य को नष्ट क्योंकि यह अब और (रोटेशन, वापस बटन) यूआई का हिस्सा नहीं
    • धागा अभी भी चल रहा है runnable अभी भी पाशन के साथ, देखें वस्तु अभी भी क्योंकि Runnable संदर्भ नष्ट नहीं के साथ ।
    • दृश्य ऑब्जेक्ट का Context का उदाहरण है, और यह संदर्भ Activity है।

    तो इस अनुक्रम के अंत में आपकी गतिविधि जीसी, एकेए द्वारा एकत्रित कचरा नहीं होगी: मेमोरी लीक!

    +0

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

    +1

    @martynmlostekk से पूरी मेमोरी लीक कहानी को समझना चाहता हूं, मेरे उत्तर के अंत में मेरा संपादन देखें। – Budius

    +0

    एक और सवाल। क्या मैं 'postInvalidate' का उपयोग कर सकता हूं? या 'postInvalidate' और' postInvalidateOnAnimation' 'के बीच क्या अंतर है? –

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