2013-01-24 16 views
5

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

मेरी जावा कोड में मैं इतना (बॉयलरप्लेट सामान सादगी के लिए निकाला गया) की तरह वर्गों लोड हो रहा हूँ:

Class clazz = groovyClassLoader.loadClass(className, true, false, true); 
instance = clazz.newInstance(); 

और मैं तो classloader कैश, metaregistry, आदि को साफ़ करके ग्रूवी कक्षाएं गतिशील फिर से लोड:

for (Class c : groovyClassLoader.getLoadedClasses()){ 
    GroovySystem.getMetaClassRegistry().removeMetaClass(c); 
} 
groovyClassLoader.clearCache(); 

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

तो मैं इसे चलाने के लिए, 128 के लिए maxpermsize की स्थापना तो मैं व्यवहार रिसाव हो और यह OOM त्रुटियों permgen:

memory profile for 128m permgen

हालांकि, अगर मैं इसे फिर से चलाने और 256M के लिए maxpermsize वृद्धि, तो सब है अच्छा और यह हमेशा के चला सकते हैं (इस छवि को 1 घंटा है, लेकिन मैं पुनः लोड के हजारों कर रात में इसे चलाने की है):

enter image description here

किसी को भी किसी भी समान व्यवहार के पार चलो गया है? या कोई विचार है? यह भी अजीब लगता है कि पहले उदाहरण में, स्मृति वृद्धि स्थिर वृद्धि के बजाय चरणों में कूद जाती है।

+0

आपके 256 मीटर परम आरेख से पता चलता है कि आपके प्रोग्राम को चलाने के लिए 150 एम परम जीन की आवश्यकता है। मेरे ऐप में, मैं लीकिंग को रोकने के लिए gclovy को .class संकलित करता हूं। हालांकि, मेटाक्लासों ने भी बहुत सारे परमजन (लगभग 100 मीटर) – farmer1992

+0

@ किसान 1 9 2 9 - हां लिया, लेकिन अगर मैं किसी भी समय 256 परिदृश्य में एक हेपडम्प लेता हूं तो मुझे कई डुप्लिकेट कक्षाएं लोड होंगी (उदाहरण के गले में यह दिखा सकता है कि उस समय लोड प्रत्येक ग्रोवी वर्ग के ~ 20 संस्करण हैं)। इसके अलावा, 128 ग्राफ़ में, लाइन में प्रत्येक चरण एक रीलोड के लिए कम या कम से संबंधित है - ताकि आप देख सकें कि पूरे सेट को पुनः लोड करने से केवल 5 5 एमबी की तरह दिखने वाले परमजन बढ़ जाता है) – rhinds

उत्तर

3

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

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

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

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

अधिक से अधिक व्यवहार को समझाने के लिए मुझे वर्ग लोडिंग और अनलोडिंग के लिए JVM के आउटपुट की आवश्यकता होगी। मुझे लगता है कि एप्लिकेशन 128 एमबी में सिद्धांत में चला सकता है।यदि आप 16:17:30 से पहले कम बिंदु पर 128 एमबी-ग्राफ़ देखते हैं और एक पहले, तो आप पहले एक के रूप में कम से कम एक नोटिस कर सकते हैं। इसका मतलब है कि उस समय कोड से पहले बिंदु पहले बिंदु से अधिक कक्षाओं को उतार दिया था। JVM एक वर्ग को निकालने का निर्णय लेने में नि: शुल्क है और सिद्धांतों को अनलोड किए जाने वाले सभी वर्गों को हमेशा हटा नहीं देता है। जब आप कर सकते हैं और प्रदर्शन कर सकते हैं, तो कक्षाओं को उतारने के बीच आपके पास व्यापार-बंद होना चाहिए।

+0

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

+0

खैर, मैं निश्चित रूप से जानने का दावा नहीं करूंगा, लेकिन अगर मुझे सही याद है तो संदर्भ में उपयोग की तरह कुछ गिनती है, यह समझने के लिए कि अक्सर उपयोग किए जाने वाले संदर्भ बाद में एकत्र किए जाते हैं। शायद यह ऐसा कुछ है। लेकिन यह वास्तव में केवल अटकलें है। एक हॉटस्पॉट इंजीनियर शायद तर्क को समझा सकता है, लेकिन यहां तक ​​कि वे हमेशा नहीं कर सकते;) – blackdrag

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