2008-10-24 17 views
11

मैं java.util.HashMap<java.awt.Point, Segment> में लगभग 50,000 ऑब्जेक्ट्स (और इसलिए 50,000 कुंजी) डालने की कोशिश कर रहा हूं। हालांकि, मैं आउटऑफमेमरी अपवाद प्राप्त करता रहता हूं। (Segment मेरी अपनी कक्षा है - बहुत हल्का वजन - एक String फ़ील्ड, और 3 int फ़ील्ड)।हैश मैप में 50,000 ऑब्जेक्ट्स डालने पर मुझे आउटऑफमेमरी त्रुटि क्यों मिलती है?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at java.util.HashMap.resize(HashMap.java:508) 
    at java.util.HashMap.addEntry(HashMap.java:799) 
    at java.util.HashMap.put(HashMap.java:431) 
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

यह काफी हास्यास्पद लगता है के बाद से मैं देख रहा हूँ मशीन पर उपलब्ध स्मृति के बहुत सारे है कि वहाँ - दोनों में नि: शुल्क रैम और आभासी स्मृति के लिए HD अंतरिक्ष।

क्या यह संभव है कि जावा कुछ कठोर स्मृति आवश्यकताओं के साथ चल रहा है? क्या मैं इन्हें बढ़ा सकता हूं?

क्या HashMap के साथ कुछ अजीब सीमा है? क्या मुझे अपना खुद का कार्यान्वयन करना होगा? क्या कोई अन्य वर्ग देखने लायक है?

(मैं 2GB RAM के साथ एक इंटेल मशीन पर जावा 5 चला रहा हूँ OS X 10.5 के तहत।)

उत्तर

20

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

आप प्रोग्राम कर सकते हैं कि Runtime कक्षा का उपयोग कर कितनी मेमोरी उपलब्ध है।

// Get current size of heap in bytes 
long heapSize = Runtime.getRuntime().totalMemory(); 

// Get maximum size of heap in bytes. The heap cannot grow beyond this size. 
// Any attempt will result in an OutOfMemoryException. 
long heapMaxSize = Runtime.getRuntime().maxMemory(); 

// Get amount of free memory within the heap in bytes. This size will increase 
// after garbage collection and decrease as new objects are created. 
long heapFreeSize = Runtime.getRuntime().freeMemory(); 

(Java Developers Almanac से उदाहरण)

यह भी आंशिक रूप से Frequently Asked Questions About the Java HotSpot VM में संबोधित किया जाता है, और Java 6 GC Tuning page में।

+0

मैं वर्तमान आकार को कैसे निर्धारित करूं ताकि मैं भविष्य के लिए जान सकूं? धन्यवाद! –

+0

बहुत अजीब हालांकि आपके पास ऐसी छोटी याददाश्त उपलब्ध है कि आप हैश में 50000 छोटी वस्तुएं नहीं जोड़ सके। इतना ज्यादा नहीं लगता है। –

+0

धन्यवाद! इसे 2048 एमबी तक पंप करना और मेरा कार्यक्रम आखिरकार निष्पादन खत्म कर देता है! Haha। वाह। –

2

आप शायद जब जावा शुरू कर झंडा -Xmx512m या कुछ बड़ी संख्या सेट करना होगा। मुझे लगता है कि 64 एमबी डिफ़ॉल्ट है।

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

1

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

+0

एक डिज़ाइन विकल्प जो मेरे दिमाग को चकमा देता है। –

+0

@ फ्रैंक क्रूगर: यह विकल्प एक अधिक कुशल कचरा-संग्राहक को लागू करने के लिए बनाया गया था। एक निश्चित अधिकतम आकार इस चीज़ को अनुकूलित करने में मदद करता है। – Mnementh

1

डिफ़ॉल्ट रूप से, JVM एक सीमित ढेर स्थान का उपयोग करता है। सीमा JVM कार्यान्वयन-निर्भर है, और यह स्पष्ट नहीं है कि आप किस जेवीएम का उपयोग कर रहे हैं। विंडोज़ के अलावा ओएस के अलावा, 2 जीबी या उससे अधिक मशीन वाली 32-बिट सन जेवीएम भौतिक मेमोरी के 1/4 के डिफ़ॉल्ट अधिकतम ढेर आकार या आपके मामले में 512 एमबी का उपयोग करेगा। हालांकि, "क्लाइंट" मोड के लिए डिफ़ॉल्ट JVM केवल 64 एमबी अधिकतम ढेर आकार है, जो आप चला सकते हैं। अन्य विक्रेता के जेवीएम अलग-अलग डिफ़ॉल्ट का चयन कर सकते हैं।

बेशक, आप -Xmx<NN>m विकल्प java के साथ स्पष्ट रूप से ढेर सीमा निर्दिष्ट कर सकते हैं, जहां <NN> ढेर के लिए मेगाबाइट्स की संख्या है।

किसी न किसी अनुमान के रूप में, आपकी हैश तालिका केवल 16 एमबी का उपयोग करनी चाहिए, इसलिए ढेर पर कुछ अन्य बड़ी वस्तुएं होनी चाहिए। यदि आप TreeMap में Comparable कुंजी का उपयोग कर सकते हैं, तो यह कुछ स्मृति को बचाएगा। अधिक जानकारी के लिए

"Ergonomics in the 5.0 JVM" देखें।

+0

सीमा को ऊपर उठाने से काम किया है, लेकिन TreeMap के संदर्भ के लिए बहुत बहुत धन्यवाद। –

3

यदि आप पहले से ऑब्जेक्ट्स की संख्या जानते हैं तो कोशिश करने के लिए एक और चीज डिफ़ॉल्ट नो-एर्ग के बजाय हैश मैप (इंट क्षमता, डबल लोडफैक्टर) कन्स्ट्रक्टर का उपयोग करना है जो (16,0.75) के डिफ़ॉल्ट का उपयोग करता है। यदि आपके हैशैप में तत्वों की संख्या (क्षमता * लोडफैक्टर) से अधिक है तो हैश मैप में अंतर्निहित सरणी 2 की अगली शक्ति में बदल दी जाएगी और तालिका को फिर से हटा दिया जाएगा। इस सरणी को स्मृति के एक संगत क्षेत्र की भी आवश्यकता होती है, उदाहरण के लिए यदि आप 32768 से 65536 आकार सरणी से दोगुना हो रहे हैं तो आपको स्मृति के 256 केबी हिस्से की आवश्यकता होगी। अतिरिक्त आवंटन और जुर्माना को खत्म करने से बचने के लिए, शुरुआत से ही एक बड़ी हैश तालिका का उपयोग करें। इससे यह संभावना कम हो जाएगी कि आपके पास मानचित्र के अनुकूल होने के लिए पर्याप्त मेमोरी का एक संगत क्षेत्र नहीं होगा।

3

कार्यान्वयन आमतौर पर सरणी द्वारा समर्थित होते हैं। Arrays स्मृति के निश्चित आकार ब्लॉक हैं। हैशमैप कार्यान्वयन 100 ऑब्जेक्ट्स कहकर, इन सरणी में से किसी एक में डेटा को संग्रहीत करके शुरू होता है।

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

आमतौर पर कोड जो बड़ी संख्या में वस्तुओं को कॉपी करके मानचित्र की क्षमता को बढ़ाता है, ऐसी समस्या का कारण है। "गूंगा" कार्यान्वयन और स्मार्ट वाले लोग हैं जो विकास या लोड कारक का उपयोग करते हैं जो पुरानी सरणी के आकार के आधार पर नई सरणी का आकार निर्धारित करता है। कुछ कार्यान्वयन इन पैरामीटर को छुपाते हैं और कुछ ऐसा नहीं करते हैं ताकि आप उन्हें हमेशा सेट न कर सकें। समस्या यह है कि जब आप इसे सेट नहीं कर सकते हैं, तो यह कुछ डिफ़ॉल्ट लोड कारक चुनता है, जैसे कि 2. इसलिए नई सरणी पुरानी के आकार से दोगुनी है। अब आपके अनुमानित 50k मानचित्र में 100k का बैकिंग सरणी है।

यह देखने के लिए देखें कि क्या आप लोड कारक को 0.25 या कुछ कम कर सकते हैं। इससे अधिक हैश नक्शा टकराव का कारण बनता है जो प्रदर्शन को नुकसान पहुंचाता है लेकिन आप स्मृति की बाधा को मार रहे हैं और ऐसा करने की आवश्यकता है। इस निर्माता

उपयोग:

(http://java.sun.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int, नाव))

+0

+1 यह कुछ समस्या बताता है जिसका सामना कर रहा हूं! – bguiz

1

जावा ढेर अंतरिक्ष डिफ़ॉल्ट रूप से सीमित है, लेकिन है कि अभी भी (हालांकि कितना बड़ा अपने 50000 खंड नहीं है)

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

मुझे आश्चर्य है कि आप TreeMap के बजाय हैश मैप का उपयोग क्यों कर रहे हैं? भले ही अंक दो आयामी हैं, फिर भी आप उन्हें तुलनात्मक कार्य के साथ उपclass कर सकते हैं और फिर लॉग (एन) लुकअप करते हैं।

7

कुछ लोग स्मृति आवश्यकताओं को कसने के लिए हैश मैप के पैरामीटर को बदलने का सुझाव दे रहे हैं। मैं अनुमान लगाने के बजाय माप का सुझाव दूंगा; यह ओओएमई के कारण कुछ और हो सकता है।विशेष रूप से, मैं या तो NetBeans Profiler या VisualVM (जो जावा 6 के साथ आता है, का उपयोग करने का सुझाव देता हूं, लेकिन मुझे लगता है कि आप जावा 5 के साथ फंस गए हैं)।

1

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

+0

दिलचस्प, क्या आप इस केविन पर विस्तार कर सकते हैं? –