java - जावा के साथ मेमोरी रिसाव बनाना




memory memory-leaks (20)

मेरे पास अभी एक साक्षात्कार था, और मुझे जावा के साथ मेमोरी रिसाव बनाने के लिए कहा गया था। कहने की जरूरत नहीं है कि मुझे बहुत मूर्ख महसूस हुआ है कि कोई भी कैसे बनाना शुरू करना है इस पर कोई सुराग नहीं है।

एक उदाहरण क्या होगा?


आप sun.misc.Unsafe क्लास के साथ मेमोरी लीक बनाने में सक्षम हैं। वास्तव में इस सेवा वर्ग का प्रयोग विभिन्न मानक वर्गों में किया जाता है (उदाहरण के लिए java.nio कक्षाओं में)। आप सीधे इस वर्ग का उदाहरण नहीं बना सकते हैं , लेकिन आप ऐसा करने के लिए प्रतिबिंब का उपयोग कर सकते हैं

कोड ग्रहण आईडीई में संकलित नहीं है - इसे कमांड javac का उपयोग करके संकलित करें (संकलन के दौरान आपको चेतावनियां मिलेंगी)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;


public class TestUnsafe {

    public static void main(String[] args) throws Exception{
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field f = unsafeClass.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        System.out.print("4..3..2..1...");
        try
        {
            for(;;)
                unsafe.allocateMemory(1024*1024);
        } catch(Error e) {
            System.out.println("Boom :)");
            e.printStackTrace();
        }
    }

}

एक साधारण बात यह है कि हैशसेट को गलत (या अस्तित्वहीन) hashCode() या equals() , और फिर "डुप्लीकेट" जोड़ना जारी रखें। डुप्लीकेट को अनदेखा करने के बजाय, यह सेट केवल बढ़ेगा और आप उन्हें हटाने में सक्षम नहीं होंगे।

यदि आप इन बुरी कुंजियों / तत्वों को अपने आस-पास लटकना चाहते हैं तो एक स्थिर क्षेत्र का उपयोग कर सकते हैं

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.

जवाब पूरी तरह से इस बात पर निर्भर करता है कि साक्षात्कारकर्ता ने सोचा कि वे क्या पूछ रहे थे।

जावा रिसाव बनाने के लिए अभ्यास में यह संभव है? बेशक यह है, और अन्य उत्तरों में कई उदाहरण हैं।

लेकिन कई मेटा-प्रश्न हैं जिन्हें पूछा जा सकता है?

  • एक सैद्धांतिक रूप से "सही" जावा कार्यान्वयन लीक के लिए कमजोर है?
  • क्या उम्मीदवार सिद्धांत और वास्तविकता के बीच अंतर को समझता है?
  • क्या उम्मीदवार समझता है कि कचरा संग्रह कैसे काम करता है?
  • या एक आदर्श मामले में कचरा संग्रह कैसे काम करना चाहिए?
  • क्या वे जानते हैं कि वे देशी इंटरफेस के माध्यम से अन्य भाषाओं को कॉल कर सकते हैं?
  • क्या वे उन अन्य भाषाओं में स्मृति को रिसाव करने के बारे में जानते हैं?
  • क्या उम्मीदवार यह भी जानता है कि मेमोरी प्रबंधन क्या है, और जावा में दृश्य के पीछे क्या चल रहा है?

मैं आपके मेटा-प्रश्न को पढ़ रहा हूं "इस साक्षात्कार की स्थिति में मैं क्या जवाब दे सकता था"। और इसलिए, मैं जावा के बजाय साक्षात्कार कौशल पर ध्यान केंद्रित करने जा रहा हूं। मेरा मानना ​​है कि जावा रिसाव को बनाने के तरीके के बारे में जानने की आवश्यकता के मुकाबले एक साक्षात्कार में किसी प्रश्न के उत्तर को नहीं जानने की स्थिति को दोहराने की संभावना अधिक है। तो, उम्मीद है कि यह मदद करेगा।

साक्षात्कार के लिए आप विकसित कर सकते हैं सबसे महत्वपूर्ण कौशल में से एक है सक्रिय रूप से प्रश्नों को सुनना और साक्षात्कारकर्ता के साथ अपने इरादे को निकालने के लिए काम करना सीखना। न केवल यह आपको उनके प्रश्न का उत्तर देने के तरीके का उत्तर देता है, बल्कि यह भी दिखाता है कि आपके पास कुछ महत्वपूर्ण संचार कौशल हैं। और जब यह कई समान प्रतिभाशाली डेवलपर्स के बीच एक विकल्प के लिए आता है, तो मैं उस व्यक्ति को किराए पर लेता हूं जो हर बार जवाब देने से पहले सुनता है, सोचता है और समझता है।


नीचे एक गैर-स्पष्ट मामला होगा जहां जावा लीक, भुलाए गए श्रोताओं के मानक मामले, स्थिर संदर्भ, हॉगहैप्स में बोगस / संशोधित कुंजी के अलावा, या केवल उनके जीवन चक्र को समाप्त करने के किसी भी मौके के बिना फंसे धागे।

  • File.deleteOnExit() - हमेशा स्ट्रिंग को लीक करता है, यदि स्ट्रिंग एक सबस्ट्रिंग है, तो रिसाव भी बदतर है (अंतर्निहित char [] भी लीक किया जाता है) - जावा 7 में सबस्ट्रिंग भी char[] प्रतिलिपि बनाता है, इसलिए बाद में लागू नहीं होता है ; @ डैनियल, वोटों की कोई ज़रूरत नहीं है, हालांकि।

मैं ज्यादातर अप्रबंधित धागे के खतरे को दिखाने के लिए धागे पर ध्यान केंद्रित करूंगा, स्विंग को छूना भी नहीं चाहता हूं।

  • Runtime.addShutdownHook और हटाएं ... और उसके बाद भी हटाए गए ShutdownHook के साथ थ्रेड ग्रुप क्लास में अनस्टार्ट किए गए थ्रेड के बारे में एक बग के कारण यह एकत्र नहीं हो सकता है, प्रभावी रूप से थ्रेड ग्रुप को रिसाव कर सकता है। जी ग्रुप गॉसिप राउटर में रिसाव है।

  • बनाना, लेकिन शुरू नहीं करना, एक Thread उपरोक्त के समान श्रेणी में जाता है।

  • थ्रेड बनाना ContextClassLoader और AccessControlContext , प्लस ThreadGroup और किसी भी InheritedThreadLocal ThreadGroup को ThreadGroup , ये सभी संदर्भ ThreadGroup द्वारा लोड किए गए पूरे वर्गों और सभी स्थिर संदर्भों और जावा-जे के साथ संभावित लीक होते हैं। प्रभाव विशेष रूप से पूरे jucExecutor ढांचे के साथ दिखाई देता है जिसमें एक सुपर सरल ThreadFactory इंटरफ़ेस है, फिर भी अधिकांश डेवलपर्स के पास खतरनाक खतरे का कोई संकेत नहीं है। इसके अलावा कई पुस्तकालय अनुरोध पर थ्रेड शुरू करते हैं (जिस तरह से कई उद्योग लोकप्रिय पुस्तकालय)।

  • ThreadLocal कैश; वे कई मामलों में बुराई हैं। मुझे यकीन है कि थ्रेडलोकल के आधार पर हर किसी ने सरल कैशों को देखा है, अच्छी तरह से बुरी खबर: अगर धागा संदर्भ क्लासलोडर के जीवन की अपेक्षा से अधिक जा रहा है, तो यह एक शुद्ध अच्छी छोटी रिसाव है। वास्तव में जरूरत होने तक थ्रेडलोकल कैश का उपयोग न करें।

  • ThreadGroup.destroy() कॉल ThreadGroup.destroy() जब थ्रेड ग्रुप में कोई थ्रेड नहीं होता है, लेकिन यह अभी भी बच्चे थ्रेड ग्रुप को रखता है। एक खराब रिसाव जो थ्रेड ग्रुप को अपने माता-पिता से हटाने के लिए रोक देगा, लेकिन सभी बच्चे अनगिनत हो जाते हैं।

  • WeakHashMap का उपयोग करना और मान (इन) सीधे कुंजी का संदर्भ देता है। एक ढेर डंप के बिना यह मुश्किल है। यह सभी विस्तारित Weak/SoftReference पर लागू होता है जो संरक्षित ऑब्जेक्ट पर एक कठिन संदर्भ रख सकता है।

  • HTTP (एस) प्रोटोकॉल के साथ java.net.URL का उपयोग करना और संसाधन को (!) से लोड करना। यह एक विशेष है, KeepAliveCache सिस्टम थ्रेड ग्रुप में एक नया धागा बनाता है जो वर्तमान थ्रेड के संदर्भ क्लासलोडर को रिसाव करता है। धागा पहले अनुरोध पर बनाया गया है जब कोई जीवित धागा मौजूद नहीं है, तो या तो आप भाग्यशाली हो सकते हैं या बस रिसाव कर सकते हैं। रिसाव जावा 7 में पहले से तय है और थ्रेड बनाता है जो कोड सही ढंग से संदर्भ क्लासलोडर को हटा देता है। कुछ और मामले हैं ( ImageFetcher की तरह , भी तय ) समान धागे बनाने के।

  • InflaterInputStream का उपयोग InflaterInputStream new java.util.zip.Inflater() के लिए (उदाहरण के लिए PNGImageDecoder ) और inflater के end() को कॉल नहीं करना। खैर, अगर आप केवल new साथ कन्स्ट्रक्टर में पास करते हैं, तो कोई मौका नहीं है ... और हाँ, स्ट्रीम पर close() को कॉल करना अगर inflator पैरामीटर के रूप में मैन्युअल रूप से पास हो जाता है तो inflater को बंद नहीं करता है। यह एक वास्तविक रिसाव नहीं है क्योंकि इसे अंतिमकर्ता द्वारा जारी किया जाएगा ... जब इसे आवश्यक समझा जाता है। उस क्षण तक यह मूल स्मृति को इतनी बुरी तरह खाता है कि यह लिनक्स oom_killer को दंड के साथ प्रक्रिया को मारने का कारण बन सकता है। मुख्य मुद्दा यह है कि जावा में अंतिम रूप बहुत अविश्वसनीय है और जी 1 ने इसे 7.0.2 तक खराब कर दिया है। कहानी का नैतिक: जैसे ही आप कर सकते हैं मूल संसाधनों को छोड़ दें; फाइनलाइज़र बहुत खराब है।

  • java.util.zip.Deflater साथ एक ही मामला। यह एक बहुत खराब है क्योंकि Deflater जावा में भूख लगी है, यानी हमेशा 15 बिट्स (अधिकतम) और 8 मेमोरी लेवल का उपयोग करता है (9 अधिकतम है) कई सैकड़ों देशी मूल स्मृति आवंटित करता है। सौभाग्य से, Deflater व्यापक रूप से उपयोग नहीं किया जाता है और मेरे ज्ञान के लिए जेडीके में कोई दुरुपयोग नहीं है। यदि आप मैन्युअल रूप से Inflater या Inflater बनाते हैं तो हमेशा end() कॉल करें। पिछले दो का सबसे अच्छा हिस्सा: आप उन्हें उपलब्ध सामान्य प्रोफाइलिंग टूल के माध्यम से नहीं ढूंढ सकते हैं।

(मैं अनुरोध पर सामना करने के कुछ और समय बर्बाद कर सकते हैं।)

खुशकिस्मत रहें और सुरक्षित रहें; लीक बुराई हैं!


यहां सबसे अधिक उदाहरण "बहुत जटिल" हैं। वे किनारे के मामले हैं। इन उदाहरणों के साथ, प्रोग्रामर ने गलती की (जैसे बराबर / हैशकोड को फिर से परिभाषित नहीं करना), या JVM / JAVA (स्थिर के साथ कक्षा का भार ...) के कोने मामले से काटा गया है। मुझे लगता है कि उदाहरण के प्रकार का साक्षात्कारकर्ता नहीं चाहता है या यहां तक ​​कि सबसे आम मामला भी है।

लेकिन मेमोरी लीक के लिए वास्तव में सरल मामले हैं। कचरा कलेक्टर केवल तभी मुक्त होता है जिसे अब संदर्भित नहीं किया जाता है। हम जावा डेवलपर्स के रूप में स्मृति की परवाह नहीं करते हैं। आवश्यकता होने पर हम इसे आवंटित करते हैं और इसे स्वचालित रूप से मुक्त कर देते हैं। ठीक।

लेकिन किसी भी लंबे समय तक रहने वाले आवेदन में राज्य साझा किया जाता है। यह कुछ भी हो सकता है, statics, singletons ... अक्सर गैर-तुच्छ अनुप्रयोग जटिल वस्तुओं ग्राफ बनाने के लिए होते हैं। सिर्फ एक ऑब्जेक्ट को निकालने के लिए भूलना भूल जाते हैं या एक संग्रह से एक वस्तु को हटाने के लिए भूल जाते हैं, स्मृति मेमोरी बनाने के लिए पर्याप्त है।

निस्संदेह सभी तरह के श्रोताओं (जैसे यूआई श्रोताओं), कैश, या किसी भी लंबे समय तक साझा राज्य स्थिति मेमोरी रिसाव का उत्पादन करती है अगर सही तरीके से संभाला नहीं जाता है। क्या समझा जाएगा कि यह जावा कोने का मामला नहीं है, या कचरा कलेक्टर के साथ एक समस्या है। यह एक डिजाइन समस्या है। हम डिज़ाइन करते हैं कि हम एक लंबे समय तक ऑब्जेक्ट में श्रोता जोड़ते हैं, लेकिन जब हम अब आवश्यकता नहीं रखते हैं तो हम श्रोता को नहीं हटाते हैं। हम वस्तुओं को कैश करते हैं, लेकिन हमारे पास कैश से उन्हें हटाने की कोई रणनीति नहीं है।

हमारे पास शायद एक जटिल ग्राफ है जो गणना के द्वारा आवश्यक पिछले राज्य को संग्रहीत करता है। लेकिन पिछला राज्य पहले से ही राज्य से जुड़ा हुआ है।

जैसे हमें SQL कनेक्शन या फ़ाइलों को बंद करना है। हमें शून्य से उचित संदर्भ सेट करने और संग्रह से तत्वों को हटाने की आवश्यकता है। हमारे पास उचित कैशिंग रणनीतियों (अधिकतम मेमोरी आकार, तत्वों की संख्या, या टाइमर) होंगे। सभी ऑब्जेक्ट्स जो श्रोता को अधिसूचित करने की अनुमति देते हैं उन्हें एक ऐडलिस्टर और निकालने के लिए विधि प्रदान करना चाहिए। और जब इन नोटिफायरों का अब उपयोग नहीं किया जाता है, तो उन्हें अपनी श्रोता सूची को साफ़ करना होगा।

एक स्मृति रिसाव वास्तव में वास्तव में संभव है और पूरी तरह से अनुमान लगाया जा सकता है। विशेष भाषा सुविधाओं या कोने के मामलों की कोई ज़रूरत नहीं है। मेमोरी लीक या तो एक संकेतक हैं कि कुछ शायद गायब हो या डिजाइन समस्याओं की भी हो।


शायद संभावित स्मृति रिसाव के सबसे सरल उदाहरणों में से एक, और इससे कैसे बचें, ArrayList.remove (int) का कार्यान्वयन है:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    elementData[--size] = null; // (!) Let gc do its work

    return oldValue;
}

यदि आप इसे स्वयं लागू कर रहे थे, तो क्या आपने उस सरणी तत्व को साफ़ करने के लिए सोचा होगा जो अब उपयोग नहीं किया गया है ( elementData[--size] = null )? उस संदर्भ में एक विशाल वस्तु जीवित रह सकती है ...


स्टेटिक फ़ील्ड होल्डिंग ऑब्जेक्ट रेफरेंस [एएसपी फाइनल फील्ड]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

लंबी स्ट्रिंग पर String.intern() को कॉल करना

String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(खुलासा) खुली धाराएं (फ़ाइल, नेटवर्क आदि ...)

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

अनलॉक कनेक्शन

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

वे क्षेत्र जो जेवीएम के कचरा कलेक्टर से पहुंच योग्य नहीं हैं , जैसे मूल तरीकों के माध्यम से आवंटित स्मृति

वेब अनुप्रयोगों में, कुछ ऑब्जेक्ट्स एप्लिकेशन स्कोप में तब तक संग्रहीत किए जाते हैं जब तक एप्लिकेशन स्पष्ट रूप से रोका या हटा दिया जाता है।

getServletContext().setAttribute("SOME_MAP", map);

गलत या अनुचित JVM विकल्प , जैसे कि आईबीएम noclassgc पर noclassgc विकल्प जो अप्रयुक्त वर्ग कचरा संग्रह को रोकता है

आईबीएम जेडीके सेटिंग्स देखें।


मुझे लगता है कि एक वैध उदाहरण एक ऐसे वातावरण में थ्रेडलोकल चर का उपयोग कर सकता है जहां धागे को पूल किया जाता है।

उदाहरण के लिए, Servlets में ThreadLocal चर का उपयोग अन्य वेब घटकों के साथ संवाद करने के लिए, कंटेनर द्वारा बनाए गए धागे और पूल में निष्क्रिय लोगों को बनाए रखने के साथ। थ्रेडलोकल वैरिएबल, अगर सही तरीके से साफ नहीं किया जाता है, तब तक वहां रहेंगे, संभवतः, वही वेब घटक उनके मूल्यों को ओवरराइट करता है।

बेशक, एक बार पहचान की, समस्या आसानी से हल किया जा सकता है।


मेमोरी रिसाव क्या है:

  • यह एक बग या खराब डिजाइन के कारण होता है
  • यह स्मृति की बर्बादी है।
  • यह समय के साथ बदतर हो जाता है।
  • कचरा कलेक्टर इसे साफ नहीं कर सकता है।

विशिष्ट उदाहरण:

वस्तुओं का एक कैश गड़बड़ चीजों के लिए एक अच्छा प्रारंभिक बिंदु है।

private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}

आपका कैश बढ़ता है और बढ़ता है। और बहुत जल्द पूरे डेटाबेस को स्मृति में चूसा जाता है। एक बेहतर डिजाइन एक LRUMap का उपयोग करता है (केवल कैश में हाल ही में प्रयुक्त वस्तुओं को रखता है)।

निश्चित रूप से, आप चीजों को और अधिक जटिल बना सकते हैं:

  • थ्रेडलोकल निर्माण का उपयोग कर ।
  • अधिक जटिल संदर्भ पेड़ जोड़ना ।
  • या तृतीय पक्ष पुस्तकालयों के कारण लीक ।

अक्सर क्या होता है:

यदि इस जानकारी ऑब्जेक्ट में अन्य ऑब्जेक्ट्स के संदर्भ हैं, जो कि फिर से अन्य ऑब्जेक्ट्स के संदर्भ हैं। एक तरह से आप इसे किसी प्रकार की मेमोरी लीक मान सकते हैं, (खराब डिजाइन के कारण)।


शायद जेएनआई के माध्यम से बाहरी देशी कोड का उपयोग कर?

शुद्ध जावा के साथ, यह लगभग असंभव है।

लेकिन यह एक "मानक" प्रकार की स्मृति रिसाव के बारे में है, जब आप अब स्मृति तक नहीं पहुंच सकते हैं, लेकिन यह अभी भी एप्लिकेशन के स्वामित्व में है। इसके बजाय आप अप्रयुक्त वस्तुओं के संदर्भ, या बाद में बंद किए बिना खुली धाराओं को संदर्भित कर सकते हैं।


http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29 माध्यम से यहां एक सरल / भयावह है ।

public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

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

मूल स्ट्रिंग के अवांछित संदर्भ को संग्रहीत करने से बचने का तरीका ऐसा कुछ करना है:

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

अतिरिक्त खराबता के लिए, आप .intern()सबस्ट्रिंग भी कर सकते हैं :

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

ऐसा करने से स्ट्रिंगलीकर उदाहरण को छोड़ दिए जाने के बाद भी मूल लम्बी स्ट्रिंग और स्मृति में व्युत्पन्न सबस्ट्रिंग दोनों को बनाए रखा जाएगा।


आप उस कक्षा की अंतिम विधि में कक्षा का एक नया उदाहरण बनाकर एक चलती स्मृति रिसाव बना सकते हैं। बोनस पॉइंट्स अगर फ़ाइनलज़र कई उदाहरण बनाता है। यहां एक साधारण कार्यक्रम है जो आपके ढेर के आकार के आधार पर कुछ सेकंड और कुछ मिनटों के बीच पूरे ढेर को लीक करता है:

class Leakee {
    public void check() {
        if (depth > 2) {
            Leaker.done();
        }
    }
    private int depth;
    public Leakee(int d) {
        depth = d;
    }
    protected void finalize() {
        new Leakee(depth + 1).check();
        new Leakee(depth + 1).check();
    }
}

public class Leaker {
    private static boolean makeMore = true;
    public static void done() {
        makeMore = false;
    }
    public static void main(String[] args) throws InterruptedException {
        // make a bunch of them until the garbage collector gets active
        while (makeMore) {
            new Leakee(0).check();
        }
        // sit back and watch the finalizers chew through memory
        while (true) {
            Thread.sleep(1000);
            System.out.println("memory=" +
                    Runtime.getRuntime().freeMemory() + " / " +
                    Runtime.getRuntime().totalMemory());
        }
    }
}

कई अलग-अलग स्थितियां स्मृति रिसाव होगी। एक का सामना करना पड़ा, जो एक नक्शा का पर्दाफाश करता है जिसे प्रकट नहीं किया जाना चाहिए और दूसरी जगह में इस्तेमाल नहीं किया जाना चाहिए।

public class ServiceFactory {

private Map<String, Service> services;

private static ServiceFactory singleton;

private ServiceFactory() {
    services = new HashMap<String, Service>();
}

public static synchronized ServiceFactory getDefault() {

    if (singleton == null) {
        singleton = new ServiceFactory();
    }
    return singleton;
}

public void addService(String name, Service serv) {
    services.put(name, serv);
}

public void removeService(String name) {
    services.remove(name);
}

public Service getService(String name, Service serv) {
    return services.get(name);
}

// the problematic api, which expose the map.
//and user can do quite a lot of thing from this api.
//for example, create service reference and forget to dispose or set it null
//in all this is a dangerous api, and should not expose 
public Map<String, Service> getAllServices() {
    return services;
}

}

// resource class is a heavy class
class Service {

}

किसी भी सर्वलेट कंटेनर (टोमकैट, जेट्टी, ग्लासफ़िश, जो भी ...) में चल रहे किसी भी वेब एप्लिकेशन को ले जाएं। पंक्ति में ऐप को 10 या 20 बार पुन: नियोजित करें (यह सर्वर की ऑटोोडोल्ड निर्देशिका में WAR को स्पर्श करने के लिए पर्याप्त हो सकता है।

जब तक किसी ने वास्तव में इसका परीक्षण नहीं किया है, तो संभावना है कि आपको कुछ पुनर्निर्माण के बाद आउटऑफमेमरी एरर मिलेगा, क्योंकि एप्लिकेशन ने खुद को साफ करने की परवाह नहीं की थी। आप इस परीक्षण के साथ अपने सर्वर में एक बग भी पा सकते हैं।

समस्या यह है कि कंटेनर का जीवनकाल आपके आवेदन के जीवनकाल से अधिक लंबा है। आपको यह सुनिश्चित करना होगा कि कंटेनर को आपके संदर्भ के ऑब्जेक्ट्स या कक्षाओं के सभी संदर्भों को कचरा इकट्ठा किया जा सकता है।

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

आपके आवेदन द्वारा शुरू किए गए थ्रेड, थ्रेडलोकल वैरिएबल, लॉगिंग एपेंडर कुछ सामान्य संदिग्ध हैं जो क्लासलोडर लीक का कारण बनते हैं।


जब तक वे समाप्त नहीं होते हैं तब तक थ्रेड एकत्र नहीं किए जाते हैं। वे कचरा संग्रह की roots के रूप में काम करते हैं । वे उन कुछ वस्तुओं में से एक हैं जिन्हें उनके बारे में भूलकर या संदर्भों को समाशोधन करके पुनः प्राप्त नहीं किया जाएगा।

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

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

एक खिलौना उदाहरण के रूप में:

static void leakMe(final Object object) {
    new Thread() {
        public void run() {
            Object o = object;
            for (;;) {
                try {
                    sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {}
            }
        }
    }.start();
}

System.gc()आप जो भी पसंद करते हैं उसे कॉल करें , लेकिन पारित वस्तु leakMeकभी मर जाएगी नहीं।

(* संपादित *)


जीयूआई कोड में इसका एक आम उदाहरण है जब विजेट / घटक बनाते हैं और कुछ स्थैतिक / एप्लिकेशन स्कोप्ड ऑब्जेक्ट में श्रोता जोड़ते हैं और फिर विजेट नष्ट होने पर श्रोता को हटा नहीं देते हैं। न केवल आपको स्मृति रिसाव मिलती है, बल्कि जब भी आप घटनाओं को आग लगाना सुनते हैं तो प्रदर्शन भी प्रभावित होता है, आपके सभी पुराने श्रोताओं को भी बुलाया जाता है।


मुझे नहीं लगता कि किसी ने अभी तक यह कहा है: आप अंतिम () विधि को ओवरराइड करके किसी ऑब्जेक्ट को पुनर्जीवित कर सकते हैं जैसे कि अंतिम () इस संदर्भ को संग्रहीत करता है। कचरा कलेक्टर केवल वस्तु पर एक बार बुलाया जाएगा ताकि उसके बाद वस्तु कभी नष्ट न हो जाए।


मैं यहां से अपना उत्तर कॉपी कर सकता हूं: जावा में मेमोरी लीक का कारण बनने का सबसे आसान तरीका?

"कंप्यूटर विज्ञान (या रिसाव, इस संदर्भ में) में एक स्मृति रिसाव तब होता है जब एक कंप्यूटर प्रोग्राम स्मृति का उपभोग करता है लेकिन इसे ऑपरेटिंग सिस्टम पर वापस छोड़ने में असमर्थ है।" (विकिपीडिया)

आसान जवाब है: आप नहीं कर सकते। जावा स्वचालित मेमोरी प्रबंधन करता है और उन संसाधनों को मुक्त करेगा जो आपके लिए आवश्यक नहीं हैं। आप इसे होने से नहीं रोक सकते हैं। यह हमेशा संसाधनों को जारी करने में सक्षम होगा। मैन्युअल मेमोरी प्रबंधन के साथ कार्यक्रमों में, यह अलग है। आप malloc () का उपयोग कर सी में कुछ स्मृति प्राप्त नहीं कर सकते हैं। स्मृति को मुक्त करने के लिए, आपको उस पॉइंटर की आवश्यकता होती है जो मॉलोक लौटा और उस पर मुफ्त () कॉल करें। लेकिन अगर आपके पास अब पॉइंटर नहीं है (अधिलेखित, या आजीवन पार हो गया है), तो आप दुर्भाग्य से इस स्मृति को मुक्त करने में असमर्थ हैं और इस प्रकार आपके पास स्मृति रिसाव है।

अब तक के सभी अन्य उत्तर मेरी परिभाषा में वास्तव में स्मृति रिसाव नहीं हैं। वे सभी लक्ष्यहीन वस्तुओं के साथ स्मृति को भरने का लक्ष्य रखते हैं। लेकिन किसी भी समय आप अभी भी बनाई गई वस्तुओं को कम कर सकते हैं और इस प्रकार स्मृति को मुक्त कर सकते हैं -> कोई लीक नहीं। acconrad का जवाब बहुत करीब आता है, हालांकि मुझे स्वीकार करना है क्योंकि उसका समाधान कचरा कलेक्टर को अंतहीन लूप में मजबूर कर "क्रैश" करने के लिए प्रभावी रूप से प्रभावी है।

लंबा जवाब यह है: आप जेएनआई का उपयोग कर जावा के लिए लाइब्रेरी लिखकर मेमोरी रिसाव प्राप्त कर सकते हैं, जिसमें मैन्युअल मेमोरी प्रबंधन हो सकता है और इस प्रकार मेमोरी लीक हो सकती है। यदि आप इस पुस्तकालय को कॉल करते हैं, तो आपकी जावा प्रक्रिया स्मृति को रिसाव कर देगी। या, आप JVM में बग्स प्राप्त कर सकते हैं, ताकि JVM स्मृति को खो देता है। शायद JVM में कीड़े हैं, कुछ ज्ञात भी हो सकते हैं क्योंकि कचरा संग्रह छोटा नहीं है, लेकिन फिर भी यह एक बग है। डिजाइन द्वारा यह संभव नहीं है। आप ऐसे जावा कोड के लिए पूछ रहे हैं जो इस तरह की एक बग से प्रभावित होता है। क्षमा करें, मुझे एक नहीं पता है और यह अगले जावा संस्करण में अब भी एक बग नहीं हो सकता है।


मैंने सोचा कि यह दिलचस्प था कि कोई भी आंतरिक वर्ग के उदाहरणों का उपयोग नहीं करता था। यदि आपके पास आंतरिक कक्षा है; यह मूल रूप से युक्त वर्ग के संदर्भ को बनाए रखता है। बेशक यह तकनीकी रूप से एक स्मृति रिसाव नहीं है क्योंकि जावा अंततः इसे साफ़ कर देगा; लेकिन यह वर्गों को अनुमानित से अधिक लंबे समय तक लटका सकता है।

public class Example1 {
  public Example2 getNewExample2() {
    return this.new Example2();
  }
  public class Example2 {
    public Example2() {}
  }
}

अब अगर आप example1 को कॉल करते हैं और उदाहरण 1 को छोड़कर उदाहरण 2 प्राप्त करते हैं, तो आप निहित रूप से अभी भी example1 ऑब्जेक्ट का लिंक लेंगे।

public class Referencer {
  public static Example2 GetAnExample2() {
    Example1 ex = new Example1();
    return ex.getNewExample2();
  }

  public static void main(String[] args) {
    Example2 ex = Referencer.GetAnExample2();
    // As long as ex is reachable; Example1 will always remain in memory.
  }
}

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


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

class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

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

इसके बाद आप एक ऑब्जेक्ट बनाने की व्याख्या कर सकते हैं जिसमें अंतर्निहित देशी संसाधन है, जैसे:

public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

फिर आप यह समझा सकते हैं कि यह तकनीकी रूप से एक स्मृति रिसाव है, लेकिन वास्तव में रिसाव मूलभूत संसाधनों को आवंटित करने वाले जेवीएम में देशी कोड के कारण होता है, जो आपके जावा कोड से मुक्त नहीं होते थे।

दिन के अंत में, एक आधुनिक जेवीएम के साथ, आपको कुछ जावा कोड लिखने की आवश्यकता है जो JVM की जागरूकता के सामान्य दायरे से बाहर एक मूल संसाधन आवंटित करता है।





memory-leaks