कैसे एक जावा मेमोरी लीक खोजने के लिए


142

आपको जावा में मेमोरी लीक कैसे मिल सकती है (उदाहरण के लिए, जेएचएटी का उपयोग करके)? मैं एक बुनियादी रूप लेने के लिए JHat में ढेर डंप को लोड करने की कोशिश की है। हालांकि, मुझे समझ में नहीं आता है कि मैं कैसे रूट संदर्भ ( रेफरी ) या जो कुछ भी कहा जाता है, उसे ढूंढने में सक्षम होना चाहिए । मूल रूप से, मैं बता सकता हूँ कि हैश टेबल प्रविष्टियों ([java.util.HashMap $ Entry या ऐसा ही कुछ) के कई सौ मेगाबाइट हैं, लेकिन सभी जगह नक्शे का उपयोग किया जाता है ... क्या बड़े मानचित्रों की खोज करने का कोई तरीका है , या शायद बड़े ऑब्जेक्ट पेड़ों की सामान्य जड़ें पाते हैं?

[संपादित करें] ठीक है, मैंने अब तक के जवाब पढ़े हैं, लेकिन चलो बस इतना कहता हूं कि मैं एक सस्ता कमीने हूं (जिसका अर्थ है कि मैं सीखने में अधिक रुचि रखता हूं कि जेपीएफ का उपयोग करने की तुलना में जेएचएटी का उपयोग कैसे करें)। इसके अलावा, JHat हमेशा उपलब्ध है क्योंकि यह JDK का हिस्सा है। जब तक बेशक JHat के साथ कोई रास्ता नहीं है लेकिन जानवर बल है, लेकिन मैं विश्वास नहीं कर सकता कि मामला हो सकता है।

इसके अलावा, मुझे नहीं लगता कि मैं वास्तव में ( सभी मानचित्र आकारों के लॉगिंग को जोड़कर) को संशोधित करने में सक्षम होगा और लीक को नोटिस करने के लिए इसे लंबे समय तक चला सकता हूं।


यह JProfiler के लिए एक और "वोट" है। यह ढेर विश्लेषण के लिए बहुत अच्छी तरह से काम करता है, एक अच्छा यूआई है, और बहुत अच्छी तरह से काम करता है। जैसा कि मैकेंजी जी 1 कहता है, जितना समय आप इन लीक्स के स्रोत की तलाश में जलाएंगे, उससे 500 डॉलर सस्ता है। जहां तक ​​उपकरणों की कीमत की बात है, यह खराब नहीं है।
जोव

Oracle का एक प्रासंगिक पृष्ठ यहाँ है: docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…
लॉरेल

जवाबों:


126

मैं जावा में मेमोरी लीक खोजने के लिए निम्नलिखित दृष्टिकोण का उपयोग करता हूं। मैंने बड़ी सफलता के साथ jProfiler का उपयोग किया है, लेकिन मेरा मानना ​​है कि रेखांकन क्षमताओं के साथ कोई विशेष उपकरण (ग्राफिकल रूप में विश्लेषण करना आसान है) काम करेगा।

  1. एप्लिकेशन को प्रारंभ करें और "स्थिर" स्थिति तक पहुंचने तक प्रतीक्षा करें, जब सभी इनिशियलाइजेशन पूरा हो जाए और एप्लिकेशन निष्क्रिय हो जाए।
  2. किसी भी कैश, DB- संबंधित प्रारंभ करने के लिए अनुमति देने के लिए कई बार स्मृति रिसाव के उत्पादन के संदेह में ऑपरेशन चलाएं।
  3. जीसी चलाएं और मेमोरी स्नैपशॉट लें।
  4. फिर से ऑपरेशन चलाएं। ऑपरेशन की जटिलता और संसाधित किए गए डेटा के आकार के आधार पर कई बार कई बार चलाने की आवश्यकता हो सकती है।
  5. जीसी चलाएं और मेमोरी स्नैपशॉट लें।
  6. 2 स्नैपशॉट के लिए एक अंतर चलाएँ और इसका विश्लेषण करें।

मूल रूप से विश्लेषण को सबसे बड़े सकारात्मक रूप से शुरू करना चाहिए, कहना, वस्तु प्रकार और उन अतिरिक्त वस्तुओं को स्मृति में छड़ी करने का कारण बनता है।

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

मैंने विशेष रूप से अनुप्रयोगों की मेमोरी फ़ुटप्रिंट को कम करने के उद्देश्य से बहुत सी परियोजनाएँ कीं और कुछ एप्लिकेशन विशिष्ट ट्विक्स और ट्रिक के साथ इस सामान्य दृष्टिकोण ने हमेशा अच्छा काम किया।


7
अधिकांश (यदि सभी नहीं) जावा प्रोफाइलर्स आपको एक बटन के एक क्लिक के साथ जीसी को आह्वान करने का विकल्प प्रदान करते हैं। या आप अपने कोड में System.gc () को उचित स्थान से कॉल कर सकते हैं।
दिमा मालेंको

3
भले ही हम System.gc () JVM कॉल को नजरअंदाज करने का चयन करें। AFAIK यह JVM विशिष्ट है। जवाब देने के लिए +1।
अनिकेत ठाकुर

4
वास्तव में "मेमोरी स्नैपशॉट" क्या है? क्या ऐसा कुछ है जो मुझे बताएगा कि प्रत्येक ऑब्जेक्ट की संख्या मेरा कोड चल रहा है?
सूक्ति

2
मैं "ऑब्जेक्ट प्रकारों द्वारा सबसे बड़ी सकारात्मक भिन्नता से शुरू करके" कैसे "उन अतिरिक्त वस्तुओं को स्मृति में छड़ी करने का कारण बनता है" कैसे जाना है? मैं बहुत सामान्य चीजें देखता हूं जैसे int [], Object [], स्ट्रिंग, आदि। मुझे कैसे पता चलेगा कि वे कहां से आते हैं?
विटुएल

48

यहां प्रश्नकर्ता, मुझे यह कहने के लिए एक टूल मिला है कि किसी भी क्लिक का उत्तर देने में 5 मिनट का समय नहीं लगता है, जिससे संभावित मेमोरी डिस्क ढूंढना बहुत आसान हो जाता है।

चूँकि लोग कई उपकरणों का सुझाव दे रहे हैं (मैंने केवल विजुअल wm की कोशिश की, क्योंकि मुझे JDK और JProbe ट्रायल में मिला) हालांकि मुझे एक्लिप्स प्लेटफ़ॉर्म पर बने एक मुक्त / खुले स्रोत उपकरण का सुझाव देना चाहिए, मेमोरी एनालाइज़र (कभी-कभी SAP मेमोरी के रूप में संदर्भित) विश्लेषक) http://www.eclipse.org/mat/ पर उपलब्ध है ।

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

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


1
वर्थ नोटिंग: जाहिरा तौर पर जावा 5 और इसके बाद के संस्करण में, HeapDumpOnCtrlBreakवीएम पैरामीटर उपलब्ध नहीं है । मैंने अभी तक जो समाधान पाया है, वह .hprofफ़ाइल को डंप करने के लिए JMap का उपयोग करने के लिए है, जिसे मैंने फिर ग्रहण में रखा और जांच करने के लिए MAT का उपयोग किया।
बेन

1
हीप डंप प्राप्त करने के बारे में, अधिकांश प्रोफाइलर्स (JVisualVM सहित) में फ़ाइल के लिए हीप और थ्रेड दोनों को डंप करने का विकल्प शामिल है।
२०:५० पर बीबीजा ४२

13

एक उपकरण एक बड़ी मदद है।

हालांकि, ऐसे समय होते हैं जब आप किसी उपकरण का उपयोग नहीं कर सकते हैं: ढेर डंप इतना बड़ा है कि यह उपकरण को क्रैश कर देता है, आप कुछ उत्पादन वातावरण में एक मशीन का समस्या निवारण करने की कोशिश कर रहे हैं जिसमें आपके पास केवल शेल एक्सेस है, आदि।

उस स्थिति में, यह hprof डंप फ़ाइल के आसपास अपना रास्ता जानने में मदद करता है।

SITES BEGIN देखें। यह आपको दिखाता है कि कौन सी वस्तुएं सबसे अधिक मेमोरी का उपयोग कर रही हैं। लेकिन वस्तुओं को केवल एक ही प्रकार से एक साथ गांठ नहीं किया जाता है: प्रत्येक प्रविष्टि में एक "ट्रेस" आईडी भी शामिल है। तब आप उस "TRACE nnnn" के लिए खोज कर सकते हैं कि स्टैक के शीर्ष कुछ फ्रेम देखने के लिए जहां ऑब्जेक्ट आवंटित किया गया था। अक्सर, एक बार जब मैं देखता हूं कि ऑब्जेक्ट कहां आवंटित किया गया है, तो मुझे एक बग मिल जाता है और मैं काम कर रहा हूं। इसके अलावा, ध्यान दें कि आप यह नियंत्रित कर सकते हैं कि ढेर में कितने फ्रेम रिकॉर्ड किए गए हैं -Xrunhprof के विकल्प के साथ।

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

चैनिंग करने के लिए, खराब ट्रेस आईडी वाली प्रविष्टियों के लिए HEAP DUMP सेक्शन में देखें। यह आपको एक OBJ या ARR प्रविष्टि में ले जाएगा, जो हेक्साडेसिमल में एक अद्वितीय वस्तु पहचानकर्ता को दिखाता है। उस आईडी की सभी घटनाओं की खोज करें, जिसे खोजने के लिए ऑब्जेक्ट का एक मजबूत संदर्भ मिला है। जब तक आप यह पता नहीं लगाते हैं कि लीक कहां है, तब तक उनमें से प्रत्येक पथ का अनुसरण करें। देखें कि एक उपकरण इतना आसान क्यों है?

मेमोरी लीक के लिए स्टेटिक सदस्य एक दोहराए गए अपराधी हैं। वास्तव में, यहां तक ​​कि एक उपकरण के बिना, स्थैतिक मानचित्र सदस्यों के लिए अपने कोड के माध्यम से देखने में कुछ मिनट खर्च करने लायक होगा। क्या कोई नक्शा बड़ा हो सकता है? क्या कुछ भी कभी अपनी प्रविष्टियों को साफ करता है?


"हीप डंप इतना बड़ा है कि यह उपकरण को क्रैश कर देता है" - मैंने जाँच की, jhatऔर MATस्पष्ट रूप से पूरे हीप डंप को मेमोरी में लोड करने की कोशिश की, और इसलिए आमतौर OutOfMemoryErrorपर बड़े डंप पर (यानी, उन अनुप्रयोगों से दुर्घटनाग्रस्त होती है जिन्हें सबसे अधिक आवश्यकता होती है ढेर विश्लेषण की! )। NetBeans Profiler संदर्भों को अनुक्रमित करने के लिए एक अलग एल्गोरिथ्म का उपयोग करता है, जो बड़े डंप पर धीमा हो सकता है, लेकिन कम से कम टूल और क्रैश में अनबाउंड मेमोरी का उपभोग नहीं करता है।
जेसी ग्लिक

10

ज्यादातर बार, एंटरप्राइज़ अनुप्रयोगों में दिए गए जावा हीप अधिकतम 12 से 16 जीबी के आदर्श आकार से बड़ा होता है। मैंने इन बड़े जावा एप्स पर सीधे नेटबीन्स प्रोफाइलर का काम करना मुश्किल पाया है।

लेकिन आमतौर पर इसकी जरूरत नहीं होती है। आप "लाइव" हीप डंप लेने के लिए jkk के साथ आने वाली jmap यूटिलिटी का उपयोग कर सकते हैं, यानि कि jmap GC चलाने के बाद हीप को डंप करेगा। आवेदन पर कुछ ऑपरेशन करें, ऑपरेशन पूरा होने तक प्रतीक्षा करें, फिर एक और "लाइव" हीप डंप लें। हेल्डपम्पों को लोड करने के लिए एक्लिप्स मैट जैसे उपकरणों का उपयोग करें, हिस्टोग्राम पर छाँटें, देखें कि कौन सी वस्तुएं बढ़ी हैं, या जो सबसे अधिक हैं, यह एक सुराग देगा।

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

इस दृष्टिकोण के साथ केवल एक समस्या है; विशाल हीप डंप, यहां तक ​​कि लाइव विकल्प के साथ, विकास की गोद में स्थानांतरित करने के लिए बहुत बड़ा हो सकता है, और खोलने के लिए पर्याप्त मेमोरी / रैम के साथ मशीन की आवश्यकता हो सकती है।

यही वह जगह है जहाँ क्लास हिस्टोग्राम तस्वीर में आता है। आप जम्प टूल के साथ एक लाइव क्लास हिस्टोग्राम डंप कर सकते हैं। यह स्मृति उपयोग का केवल वर्ग हिस्टोग्राम देगा। आमतौर पर इसमें संदर्भ को चेन करने की जानकारी नहीं होगी। उदाहरण के लिए यह शीर्ष पर चार सरणी रख सकता है। और स्ट्रिंग वर्ग नीचे कहीं। आपको कनेक्शन खुद खींचना होगा।

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

दो ढेर डंप लेने के बजाय, दो वर्ग हिस्टोग्राम लें, जैसे ऊपर वर्णित है; फिर क्लास हिस्टोग्राम की तुलना करें और जो कक्षाएं बढ़ रही हैं उन्हें देखें। देखें कि क्या आप जावा कक्षाओं को अपने आवेदन कक्षाओं से संबंधित कर सकते हैं। यह एक बहुत अच्छा संकेत देगा। यहां एक अजगर लिपि है जो आपको दो जम्प हिस्टोग्राम डंप की तुलना करने में मदद कर सकती है। histogramparser.py

अंत में JConolse और VisualVm जैसे उपकरण समय के साथ मेमोरी वृद्धि को देखने के लिए आवश्यक हैं, और देखें कि क्या मेमोरी लीक है। अंत में कभी-कभी आपकी समस्या एक मेमोरी लीक नहीं हो सकती है, लेकिन उच्च मेमोरी उपयोग। इसके लिए जीसी लॉगिंग सक्षम करें; जी 1 जीसी जैसे अधिक उन्नत और नए कॉम्पैक्ट जीसी का उपयोग करें; और आप जीसी व्यवहार को लाइव देखने के लिए jstat जैसे jdk टूल का उपयोग कर सकते हैं

jstat -gccause pid <optional time interval>

अन्य संदर्भों के लिए Google को -jhat, jmap, Full GC, Humongous आवंटन, G1GC


1
अधिक जानकारी के साथ यहाँ एक ब्लॉग पोस्ट जोड़ा गया - alexpunnen.blogspot.in/2015/06/…
एलेक्स पुन्नन

5

ऐसे उपकरण हैं जो आपके रिसाव को खोजने में आपकी मदद करते हैं, जैसे कि जेपीवियर, योरिट, एडी 4 जे या जेरोकिट मिशन कंट्रोल। आखिरी वह है जिसे मैं व्यक्तिगत रूप से सबसे अच्छा जानता हूं। किसी भी अच्छे उपकरण को आपको एक ऐसे स्तर तक ले जाने देना चाहिए जहाँ आप आसानी से पहचान सकें कि क्या लीक है, और कहाँ लीक होने वाली वस्तुओं को आवंटित किया गया है।

हैशटेबल्स का उपयोग करना, हश्माप्स या इसी तरह के कुछ तरीकों में से एक है जो आप जावा में मेमोरी को एक साथ लीक कर सकते हैं। अगर मुझे हाथ से रिसाव का पता लगाना था, तो मैं अपने हैशमैप के आकार को हमेशा के लिए प्रिंट करूंगा, और वहां से मैं वह चीज ढूंढूंगा जहां मैं आइटम जोड़ता हूं और उन्हें हटाना भूल जाता हूं।


4

खैर, आपके मानचित्रों के आकार को जोड़ते समय हमेशा कम तकनीकी समाधान होता है जब आप उन्हें संशोधित करते हैं, तो उन लॉग को खोजें जिनके लिए नक्शे उचित आकार से आगे बढ़ रहे हैं।


1

नेटबीन्स में एक अंतर्निर्मित प्रोफाइलर है।


0

आपको वास्तव में एक मेमोरी प्रोफाइलर का उपयोग करने की आवश्यकता है जो आवंटन को ट्रैक करता है। JProfiler पर एक नज़र डालें - उनकी "हीप वॉकर" सुविधा महान है, और उनके पास सभी प्रमुख जावा आईडीई के साथ एकीकरण है। यह मुफ़्त नहीं है, लेकिन यह इतना महंगा भी नहीं है (एक लाइसेंस के लिए $ 499) - आप कम परिष्कृत उपकरणों के साथ एक रिसाव खोजने के लिए संघर्ष कर रहे $ 500 का समय बहुत जल्दी जला देंगे।


0

आप कई बार कचरा संग्रहकर्ता को कॉल करने के बाद मेमोरी उपयोग के आकार को मापकर पता लगा सकते हैं:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

यदि आउटपुट संख्याएं समान थीं, तो आपके एप्लिकेशन में कोई मेमोरी लीक नहीं है, लेकिन यदि आपने मेमोरी उपयोग की संख्या (बढ़ती संख्या) के बीच अंतर देखा है, तो आपके प्रोजेक्ट में मेमोरी लीक है। उदाहरण के लिए:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

ध्यान दें कि कभी-कभी धाराओं और सॉकेट जैसी कुछ क्रियाओं द्वारा मेमोरी को रिलीज़ करने में कुछ समय लगता है। आपको पहले आउटपुट से न्याय नहीं करना चाहिए, आपको इसे एक विशिष्ट समय में परीक्षण करना चाहिए।


0

JProfiler के साथ मेमोरी लीक खोजने के बारे में इस स्क्रीन कास्ट चेकआउट करें । यह @Dima Malenko उत्तर का दृश्य विवरण है।

नोट: हालांकि JProfiler फ्रीवेयर नहीं है, लेकिन परीक्षण संस्करण वर्तमान स्थिति से निपट सकता है।


0

जैसा कि हम में से ज्यादातर लोग ग्रहण लिखने के लिए पहले से ही ग्रहण का उपयोग करते हैं, क्यों न ग्रहण में मेमोरी एनालाइजर टूल (MAT) का उपयोग करें। यह बहुत अच्छा काम करता है।

ग्रहण मेट ग्रहण आईडीई जो विश्लेषण करने के लिए उपकरण प्रदान करता है के लिए प्लग-इन का एक सेट है heap dumpsजावा अनुप्रयोग से और पहचान करने के लिए memory problemsआवेदन में।

यह डेवलपर को निम्नलिखित विशेषताओं के साथ मेमोरी लीक खोजने में मदद करता है

  1. मेमोरी स्नैपशॉट प्राप्त करना (हीप डंप)
  2. हिस्टोग्राम
  3. सेवानिवृत्त हीप
  4. डोमिनेटर ट्री
  5. जीसी रूट्स की खोज पथ
  6. निरीक्षक
  7. आम मेमोरी एंटी-पैटर्न
  8. ऑब्जेक्ट क्वेरी भाषा

यहां छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.