2012-03-03 15 views
14

मेरे पास एक जावा प्रोग्राम है जो मेरी उबंटू 10.04 मशीन पर चलता है और बिना किसी उपयोगकर्ता इंटरैक्शन के, बार-बार एक MySQL डेटाबेस से पूछताछ करता है और उसके बाद आईएमजी- और txt-files बनाता है जो डेटा से पढ़ता है डीबी। यह हजारों प्रश्नों को बनाता है और हजारों फाइलें बनाता है।जावा प्रोग्राम के मेमोरी खपत के मुद्दे

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

इतनी मेमोरी आवंटित करने के बारे में जानने के लिए मैं एक ढेर डंप का विश्लेषण करना चाहता था, इसलिए मैंने -Xms64m -Xmx128m -XX: + HeapDumpOnOutOfMemoryError के साथ प्रक्रिया शुरू की।

मेरे आश्चर्य के लिए, स्थिति पहले की तरह ही थी, कुछ घंटों के बाद कार्यक्रम सभी स्वैप आवंटित कर रहा था जो 128m के दिए गए अधिकतम से परे है।

विजुअलVM के साथ डीबग किए गए एक और रन से पता चला है कि ढेर आवंटन 128 एम के अधिकतम से अधिक नहीं है - जब आवंटित स्मृति अधिकतम अनुमानित होती है, तो इसका एक बड़ा हिस्सा फिर से जारी किया जाता है (मुझे कचरा कलेक्टर द्वारा माना जाता है)।

तो, यह लगातार बढ़ती ढेर में कोई समस्या नहीं हो सकती है।

जब स्मृति सब प्रयोग किया जाता है:

USER VIRT RES  SHR  COMMAND 
[my_id] 504m 171m 4520 java 
[my_id] 371m 162m 4368 java 

(अब तक दो "सबसे बड़ी" प्रक्रियाओं और:

   total  used  free  shared buffers  cached 
Mem:  2060180 2004860  55320   0  848 1042908 
-/+ buffers/cache:  961104 1099076 
Swap:  3227640 3227640   0 

शीर्ष निम्नलिखित पता चलता है:

मुक्त निम्न से पता चलता केवल जावा प्रक्रियाएं चल रही हैं)

मेरा पहला प्रश्न है:

  • मैं ओएस स्तर पर कैसे पता लगा सकता हूं (उदा। कमांड लाइन टूल्स के साथ) इतनी मेमोरी आवंटित कर रहा है? शीर्ष/htop मेरी मदद नहीं की है। कई मामलों में, स्मृति के खाने के समान प्रकार की कई छोटी प्रक्रियाएं: क्या समान प्रक्रियाओं को समझदारी से जोड़ने का कोई तरीका है? (मुझे पता है कि शायद विषय से दूर है, क्योंकि यह एक Linux/Ubuntu सवाल यह है, लेकिन मेरी मुख्य समस्या अभी भी जावा से संबंधित हो सकता है)

मेरा पुराना सवाल थे:

  • क्यों नहीं है शीर्ष आउटपुट में दिए गए मेरे कार्यक्रम की स्मृति खपत?
  • मैं कैसे पता लगा सकता हूं कि इतनी मेमोरी आवंटित कर रहा है?
  • यदि ढेर समस्या नहीं है, तो केवल "आवंटित कारक" ढेर है? ( स्टैक कोई समस्या नहीं होनी चाहिए क्योंकि कोई गहरी "विधि कॉल गहराई नहीं है")
  • बाहरी संसाधनों के बारे में डीबी कनेक्शन के बारे में क्या?
+0

प्रोफाइलिंग टूल का उपयोग करने का प्रयास करें: http://stackoverflow.com/a/9205812/90909 – qrtt1

+0

@ qrtt1: मैंने विजुअलVM का उपयोग किया, लेकिन इससे पता चला कि ढेर समस्या नहीं है (ऊपर देखें)। –

+1

आपको यहां एक उत्तर मिल सकता है मुझे लगता है कि http://stackoverflow.com/a/9306054/1140748 और http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_oom (बग विवरण देखें) –

उत्तर

0

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

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

तो अब मैं बार-बार अपने प्रोग्राम को शैल स्क्रिप्ट से चलाता हूं, प्रत्येक प्रक्रिया में केवल कार्यों का एक सेट निष्पादित करता है (तर्कों के माध्यम से पैरामीटर किया जाता है)। अंत में, सभी कार्यों को निष्पादित किया जा रहा है, लेकिन एक प्रक्रिया के रूप में केवल कार्यों के सबसेट को संसाधित करता है, अब कोई स्मृति समस्या नहीं होती है।

मेरे लिए पर्याप्त समाधान है। यदि आपके पास ऐसी ही समस्या है और आपके प्रोग्राम में बैच जैसी निष्पादन संरचना है तो यह एक व्यावहारिक दृष्टिकोण हो सकता है।

जब मुझे लगता है कि मैं समय के मूल कारणों की पहचान करने के लिए नए सुझावों को देखूंगा (मदद के लिए धन्यवाद!)।

1

हम्म ... आईपीसीएस का उपयोग यह जांचने के लिए करें कि साझा मेमोरी सेगमेंट खुले नहीं हैं। अपने JVM (/proc/<jvm proccess id>/fd/*) के खुले फ़ाइल वर्णनकर्ताओं की जांच करें। शीर्ष पर, स्वैप दिखाने के लिए fpFp टाइप करें और कार्य सूची का उपयोग करके सॉर्ट करें।

यह सब मैं अभी के साथ आ सकता हूं, उम्मीद है कि यह कम से कम थोड़ा सा मदद करता है।

0

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

+0

लेकिन कर्नेल पूरी तरह से जारी होने तक कर्नेल एक चलती प्रक्रिया को स्वैप नहीं करेगा ... –

+0

मुझे नहीं लगता कि मैं जो देख रहा हूं वह हो सकता है; जब ऐसा होता है तो मेरी प्रणाली वास्तव में बहुत प्रतिकूल रूप से प्रभावित होती है। जब तक सभी उपलब्ध रैम का उपयोग नहीं किया जाता है तब तक यह स्मृति का उपभोग करेगा, फिर यह स्वैप का उपयोग शुरू कर देगा। जैसा कि आप कल्पना कर सकते हैं, चीजें जल्द ही उस बिंदु पर एक बंद करने के लिए पीस! – Jan

7

यदि आपकी जावा प्रक्रिया वह है जो स्मृति लेती है और VisualVM या मेमोरी डंप में कुछ भी संदेह नहीं है तो यह मूल कोड में कहीं भी होना चाहिए - या तो JVM में या कुछ पुस्तकालयों में आप उपयोग कर रहे हैं। जेवीएम स्तर पर यह हो सकता है, उदाहरण के लिए, यदि आप एनआईओ या मेमोरी मैप की गई फाइलों का उपयोग कर रहे हैं। यदि आपके कुछ पुस्तकालय देशी कॉल का उपयोग कर रहे हैं या आप अपने डेटाबेस के लिए 4 जेडीबीसी ड्राइवर टाइप नहीं कर रहे हैं तो रिसाव वहां हो सकता है।

कुछ सुझाव:

  • कुछ विवरण मूल कोड here में मेमोरी लीक लगाने के लिए कैसे कर रहे हैं। अच्छा read भी।
  • सामान्य रूप से, सुनिश्चित करें कि आप सभी संसाधनों को ठीक से बंद कर रहे हैं (फ़ाइलें, स्ट्रीम, कनेक्शन, थ्रेड इत्यादि)। इनमें से अधिकांश कुछ बिंदु पर देशी कार्यान्वयन बुला रहे हैं तो भस्म स्मृति JVM में सीधे दिखाई नहीं हो सकता है
  • चेक संसाधनों ओएस स्तर पर भस्म - खुली फ़ाइलों, फ़ाइल वर्णनकर्ता, नेटवर्क कनेक्शन आदि की संख्या
+0

आपके उत्तर के लिए धन्यवाद। देशी कोड में लीक एक संभावना की तरह लग रहा है। हम किसी मूल देशी पुस्तकालयों का उपयोग नहीं करते हैं जिन्हें मैं जानता हूं, यहां तक ​​कि जेडीबीसी भी नहीं। हम निश्चित रूप से सभी फाइलों को बंद करने के लिए सावधान रहने की कोशिश करते हैं, लेकिन मुझे यह समझना मुश्किल लगता है कि ऐसे संसाधनों को कैसे छोड़ना एक जेवीएम का कारण बन सकता है जिसे 24 जीबी से अधिक उपभोग करने के लिए 16 जीबी ढेर दिया गया है। निश्चित रूप से ओ/एस खुली फाइलों की संख्या को सीमित करेगा इससे पहले कि फाइल हैंडल द्वारा उस स्मृति को खपत किया गया था? – Jan

+0

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

2

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

तो यह खोजने के लिए चाल लगातार ढेर डंप का उपयोग करना है, इस बात के अलावा कि आपने पूरी प्रक्रिया के लिए स्मृति वृद्धि देखी है, लेकिन इतना दूर नहीं है कि काम का एक टन चल रहा है। आप जो खोज रहे हैं वह जावा ऑब्जेक्ट की ढेर में गिना जाता है जो बढ़ते रहते हैं और मूल मेमोरी संलग्न होते हैं।

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

अधिक दुर्लभ मौकों पर, जावा ऑब्जेक्ट कचरा इकट्ठा होने के बावजूद, जावा कार्यान्वयन द्वारा लीक किया गया एक मूल संसाधन है। मैं एक बार एक विनसी 5 बग में भाग गया जहां प्रत्येक सॉकेट के करीब 4k लीक हो गए थे। इसलिए कोई जावा ऑब्जेक्ट वृद्धि नहीं हुई थी, लेकिन स्मृति उपयोग वृद्धि की प्रक्रिया थी। इन मामलों में, कुछ काउंटर बनाने और वास्तविक स्मृति बनाम मूल स्मृति के साथ वस्तुओं के जावा आवंटन का ट्रैक रखना उपयोगी होता है। फिर एक छोटी सी खिड़की पर, आप किसी भी सहसंबंध की तलाश कर सकते हैं और छोटे टेस्टकेस बनाने के लिए इनका उपयोग कर सकते हैं।

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

+0

हाय जेम्स, आपकी टिप्पणियों के लिए धन्यवाद, यह दिलचस्प है। लेकिन जैसा कि मैंने @ मैक्सिमडिम के जवाब में कहा था, हम धीरे-धीरे स्मृति की लगातार बढ़ती मात्रा का उपभोग करने वाली प्रक्रियाओं को देख रहे हैं, कई गीगाबाइट ढेर आवंटित मात्रा से अधिक हैं। हम सावधानी से फाइलों का पीछा कर रहे हैं जो अन्य कारणों से बंद नहीं हुए हैं, लेकिन मैं नहीं देख सकता कि फ़ाइलों या सॉकेट को कैसे छोड़ना स्मृति के गीगाबाइट को रिसाव कर सकता है! साथ ही, प्रश्न में प्रक्रिया एक सर्वर-साइड प्रक्रिया है, इसलिए ग्राफिक्स लाइब्रेरीज़, लोड इमेज इत्यादि का उपयोग नहीं करता है ... – Jan

+0

4K हैंडल के लिए एक समय में तेजी से जोड़ सकते हैं यदि आप बहुत सारे अनुरोधों को संभालने में कामयाब रहे हैं। यह भी सुनिश्चित करें कि आप अंततः ब्लॉक में अपने सभी परिणामसेट उदाहरण बंद कर रहे हैं। आपके जेडीबीसी कनेक्टर संस्करण के आधार पर, ResultSet ऑब्जेक्ट्स को लीक करने से मूल स्मृति प्रतिधारण हो सकता है। –

+0

उचित बिंदु, हालांकि इसमें 8 जीबी लीक करने के लिए 2k एक टुकड़े पर 2,000,000 लीक फ़ाइल हैंडल लेती हैं, और ओएस सीमाएं उससे बहुत कम सेट होती हैं। इसके अलावा, हम प्रश्न में प्रक्रिया में जेडीबीसी का उपयोग नहीं करते हैं। – Jan

1

@maximdim और @JamesBranigan बताते हैं, संभावित अपराधी आपके कोड से कुछ मूल बातचीत है। लेकिन जैसा कि आप नीचे ट्रैक करने में सक्षम नहीं हैं, जहां समस्याग्रस्त बातचीत उपलब्ध टूल का उपयोग कर रही है, आप ब्रूट फोर्स दृष्टिकोण का प्रयास क्यों नहीं करते?

आपने दो भाग प्रक्रिया को रेखांकित किया है: MySQL क्वेरी करें और फ़ाइलें लिखें। किसी भी चीज को प्रक्रिया के रूप में प्रक्रिया से बाहर रखा जा सकता है। एक टेस्ट करें: क्वेरी को हटाएं और कड़ी मेहनत की गई सामग्री को वापस कर दें। दो परीक्षण करें: क्वेरी करें, लेकिन फ़ाइलों को लिखना परेशान न करें। क्या आपके पास अभी भी रिसाव है?

आपके अन्य आवेदनों के आधार पर अन्य टेस्टेबल मामले भी हो सकते हैं।

1

क्या आप अपने "कार्य" को चलाने के लिए अलग-अलग धागे बना रहे हैं? थ्रेड बनाने के लिए प्रयुक्त स्मृति जावा ढेर से अलग है।

इसका मतलब यह है कि यदि आप -Xmx128m निर्दिष्ट करते हैं तो भी जावा प्रक्रिया द्वारा उपयोग की जाने वाली मेमोरी बहुत अधिक हो सकती है, इस पर निर्भर करता है कि आप कितने थ्रेड का उपयोग कर रहे हैं और थ्रेड स्टैक आकार (प्रत्येक थ्रेड को आवंटित आकार का आवंटित किया जाता है, -Xss)।

हाल ही में काम से उदाहरण: हम 4GB (-Xmx4G) के एक जावा ढेर था, लेकिन ओएस प्रक्रिया 6GB के ऊपर भी स्वैप स्पेस का उपयोग कर लेने वाली किया गया था, । जब मैंने cat /proc/<PID>/status के साथ प्रक्रिया की स्थिति की जांच की तो मैंने देखा कि हमारे पास 11000 धागे चल रहे थे। चूंकि हमारे पास -Xss256K सेट था, यह आसानी से समझाया गया है: 10000 धागे का मतलब 2,5 जीबी है।

0

आप कहते हैं कि आप छवि फाइलें बना रहे हैं क्या आप छवि वस्तुओं को बना रहे हैं? यदि हां, तो क्या आप इन वस्तुओं पर निपटान() को कॉल करते समय कॉल कर रहे हैं?

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

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