2012-11-23 22 views
12

मुझे लगता है कि मेरे एंड्रॉइड लाइव वॉलपेपर में मेमोरी लीक है। जब भी मैं स्क्रीन घुमाता हूं, एकत्रित मेमोरी कचरा की मात्रा 50kb तक बढ़ जाती है और वापस नहीं जाती है। मुझे लगता है कि यह एक निर्धारित भविष्य के कारण हो सकता है, इसलिए मैं यह देखने के लिए एक परिदृश्य पेश करने जा रहा हूं कि यह मामला है या नहीं।क्या एक निर्धारित भविष्य मेमोरी रिसाव का कारण बन सकता है?

मान लें कि आपके पास एक कक्षा है (चलिए इसे फू कहते हैं) जिसमें निम्नलिखित सदस्य हैं।

private ScheduledFuture<?> future; 
private final ScheduledExecutorService scheduler = Executors 
     .newSingleThreadScheduledExecutor(); 

private final Runnable runnable = new Runnable() { 
    public void run() { 
     // Do stuff 
    } 
}; 

और अब आप एक अनुसूचित भविष्य

future = scheduler.scheduleAtFixedRate(runnable, delay, speed, 
       TimeUnit.MILLISECONDS); 

भविष्य runnable के लिए एक संदर्भ रखती है निर्धारित करते हैं, और runnable माता पिता फू ऑब्जेक्ट के संदर्भ रखती है। मुझे यकीन नहीं है कि यह मामला है, लेकिन क्या इस तथ्य का अर्थ यह हो सकता है कि यदि कार्यक्रम में कुछ भी फू का संदर्भ नहीं रखता है, तो कचरा कलेक्टर अभी भी इसे एकत्र नहीं कर सकता क्योंकि एक निर्धारित भविष्य है? मैं मल्टीथ्रेडिंग पर बहुत अच्छा नहीं हूं, इसलिए मुझे नहीं पता कि मैंने जो कोड दिखाया है, उसका मतलब है कि निर्धारित कार्य ऑब्जेक्ट से अधिक समय तक जीवित रहेगा, जिसका अर्थ है कि यह कचरा एकत्र नहीं होगा।

यदि इस परिदृश्य से फू को कचरा संग्रह होने से रोका नहीं जायेगा, तो मुझे बस एक सरल स्पष्टीकरण के साथ कहा जाना चाहिए। अगर यह फू को कचरा होने से रोकता है, तो मैं इसे कैसे ठीक करूं? future.cancel(true); future = null; करना है? future = null हिस्सा अनावश्यक है?

उत्तर

5
  • या तो अपने run विधि enclosing Foo वर्ग पर निर्भर करता है और इसलिए स्वतंत्र रूप से नहीं रह सकते। उस स्थिति में मैं नहीं देखता कि आप अपने Foo gc'ed कैसे प्राप्त कर सकते हैं और निष्पादक
  • या run विधि द्वारा चलने के लिए अपना चलने योग्य "जीवित" रखें, इस अर्थ में स्थिर है कि यह राज्य पर निर्भर नहीं है आपके Foo वर्ग की, इस स्थिति में आप इसे स्थिर बना सकते हैं और यह आपके द्वारा अनुभव की जा रही समस्या को रोक देगा।

आप अपने रननेबल में बाधा को संभालने लगते नहीं हैं। इसका मतलब यह है कि यदि आप future.cancel(true) पर कॉल करते हैं तो भी आपका रननेबल चलना जारी रहेगा, जैसा कि आपने निर्धारित किया है, आपके रिसाव का कारण हो सकता है।

एक रननेबल "इंटरप्ट-फ्रेंडली" बनाने के कई तरीके हैं। या तो आप एक विधि को कॉल करते हैं जो इंटरप्टेड एक्सेप्शन (जैसे Thread.sleep() या अवरुद्ध आईओ विधि) को फेंकता है और भविष्य में रद्द होने पर InterruptedException फेंक देगा। आपको लगता है कि अपवाद को पकड़ने और साफ होने क्या साफ और बाधित राज्य को बहाल किए जाने की आवश्यकता के बाद रन विधि तुरंत बाहर निकल सकते हैं:

public void run() { 
    while(true) { 
     try { 
      someOperationThatCanBeInterrupted(); 
     } catch (InterruptedException e) { 
      cleanup(); //close files, network connections etc. 
      Thread.currentThread().interrupt(); //restore interrupted status 
     } 
    } 
}  

आप इस तरह के किसी भी तरीकों फोन नहीं है, तो मानक मुहावरा है:

public void run() { 
    while(!Thread.currentThread().isInterrupted()) { 
     doYourStuff(); 
    } 
    cleanup(); 
} 

उस स्थिति में, आपको यह सुनिश्चित करने की कोशिश करनी चाहिए कि समय की स्थिति नियमित रूप से जांच की जाए।

उन परिवर्तनों के साथ, जब आप future.cancel(true) पर कॉल करते हैं, तो आपके रननेबल को निष्पादित करने वाले थ्रेड पर एक इंटरप्ट सिग्नल भेजा जाएगा जो आपके रननेबल और आपके फू इंस्टेंस को जीसी के लिए योग्य बना देगा।

+0

मेरी रन विधि संलग्न फू क्लास पर भरोसा करती है। लेकिन जब मैं फू ऑब्जेक्ट के संदर्भ को नष्ट कर देता हूं, तो मुझे स्मृति रिसाव के कारण चलने योग्य को रोकने के लिए क्या करना होगा। क्या 'भविष्य.cancel (सत्य) 'पर्याप्त होगा? मैं पहले से ही कर रहा हूं और स्मृति रिसाव अभी भी वहां है। बेशक, इसका मतलब यह हो सकता है कि रननेबल रिसाव का स्रोत नहीं है। – gsingh2011

+0

@ gsingh2011 क्या आप वाकई सुनिश्चित हैं कि 'भविष्य.cancel (true)' आपके भविष्य को रद्द कर देता है? दूसरे शब्दों में, क्या आपके रननेबल को बाधित किया जा सकता है और जब यह बाधित होता है तो क्या यह अपना काम समाप्त कर देता है? – assylias

+0

चूंकि मुझे समझ में नहीं आता कि "क्या आपके रननेबल को बाधित किया जा सकता है" मुझे लगता है कि इसे बाधित नहीं किया जा सकता है। मुझे यह कैसे करना चाहिए? – gsingh2011

0

भविष्य में रननेबल का संदर्भ है, और रननेबल में माता-पिता Foo ऑब्जेक्ट के संदर्भ में है।मुझे यकीन नहीं है कि यह मामला है, लेकिन क्या इस तथ्य का अर्थ यह हो सकता है कि यदि कार्यक्रम में कुछ भी फू के संदर्भ में है, तो कचरा कलेक्टर अभी भी एकत्र नहीं कर सकता क्योंकि एक निर्धारित भविष्य है?

इसका एक बुरा विचार Foo है कि आप अक्सर बनाते हैं, क्योंकि आप बंद ScheduledExecutorService scheduler अपने ऐप के बंद कर देता है चाहिए क्षणिक वस्तु के कुछ प्रकार बनाने के लिए। इस प्रकार आपको फू को एक छद्म-सिंगलटन बनाना चाहिए।

कचरा कलेक्टर चक्र को समझता है ताकि एक बार जब आप Foo की निष्पादक सेवा बंद कर दें तो आपको मेमोरी के मुद्दों की संभावना नहीं होगी।

5

हालांकि इस प्रश्न का उत्तर बहुत पहले दिया गया है लेकिन this आलेख पढ़ने के बाद मैंने स्पष्टीकरण के साथ नया उत्तर पोस्ट करने का विचार किया।

क्या निर्धारित समय भविष्य में स्मृति रिसाव हो सकता है? --- हाँ

ScheduledFuture.cancel() या Future.cancel() सामान्य सूचित नहीं करता अपने Executor कि इसे रद्द कर दिया गया है और यह जब तक निष्पादन के लिए अपने समय आ गया है कतार में रहता है। यह साधारण फ्यूचर्स के लिए एक बड़ा सौदा नहीं है लेकिन ScheduledFutures के लिए एक बड़ी समस्या हो सकती है। यह सेकंड, मिनट, घंटे, दिन, सप्ताह, साल या लगभग अनिश्चित काल तक वहां रह सकता है, जिस पर देरी हो रही है।

यहां सबसे खराब स्थिति परिदृश्य वाला एक उदाहरण है। चलने योग्य और जो कुछ भी संदर्भित किया जा रहा है, वह भविष्य के रद्द होने के बाद भी Long.MAX_VALUE मिलीसेकंड के लिए कतार में रहेगा!

public static void main(String[] args) { 
    ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Hello World!"); 
     } 
    }; 

    ScheduledFuture future 
     = executor.schedule(task, 
      Long.MAX_VALUE, TimeUnit.MILLISECONDS); 

    future.cancel(true); 
} 

आप एक Profiler का उपयोग कर या ScheduledThreadPoolExecutor.shutdownNow() विधि है जो उस में एक तत्व (यह Runnable कि रद्द कर दिया गया है) के साथ एक सूची वापस आ जाएगी फोन करके देख सकते हैं।

इस समस्या का समाधान या तो अपने भविष्य के कार्यान्वयन को लिखना है या purge() विधि को हर बार और फिर कॉल करना है। कस्टम एक्जिक्यूटिव फैक्ट्री समाधान के मामले में:

public static ScheduledThreadPoolExecutor createSingleScheduledExecutor() { 
    final ScheduledThreadPoolExecutor executor 
     = new ScheduledThreadPoolExecutor(1); 

    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      executor.purge(); 
     } 
    }; 

    executor.scheduleWithFixedDelay(task, 30L, 30L, TimeUnit.SECONDS); 

    return executor; 
} 
+1

क्या आप कृपया समझा सकते हैं, इस परिदृश्य में क्या होगा? यह एक सिंगल थ्रेडेड एक्जिक्यूटर है। यदि रननेबल कार्य लंबे समय तक चल रहा कार्य है (एक दिन कहें) और देरी और अवधि उपरोक्त के समान है। क्या कार्य कतार में आता है? कार्यों के लिए कतार पर ऊपरी सीमा है? – cherryhitech