2009-12-23 10 views
30

मैं बहुत सारी खोज कर रहा हूं और मुझे पता है कि कई अन्य लोग BitmapFactory के साथ समान ओओएम मेमोरी समस्याओं का सामना कर रहे हैं। मेरा ऐप केवल Runtime.getRuntime ().totalMemory() का उपयोग कर 4 एमबी की कुल मेमोरी उपलब्ध कराता है। यदि सीमा 16 एमबी है, तो बिटमैप के लिए जगह बनाने के लिए कुल मेमोरी क्यों नहीं बढ़ती है? इसके बजाय यह एक त्रुटि फेंकता है।बिटमैप फैक्टरी ओओएम मुझे नट्स चला रहा है

मैं भी समझ में नहीं आता है, तो मैं मुक्त स्मृति Runtime.getRuntime().freeMemory() को अनुसार की 1.6MB क्यों मैं एक त्रुटि कह मिलता है "वीएम हमें 614,400 बाइट्स आवंटित नहीं दूँगी" है? मुझे लगता है कि मेरे पास उपलब्ध स्मृति है।

मेरा ऐप इस समस्या को छोड़कर पूरा हो गया है, जो दूर चला जाता है जब मैं फ़ोन रीबूट करता हूं ताकि मेरा ऐप एकमात्र चीज चल रहा हो। मैं डिवाइस परीक्षण (एंड्रॉइड 1.5) के लिए एक एचटीसी हीरो का उपयोग कर रहा हूं।

इस बिंदु पर मैं सोच रहा हूं कि किसी भी तरह से BitmapFactory का उपयोग करने से बचें।

किसी के पास इस पर कोई विचार या स्पष्टीकरण है कि क्यों VM 1.6 एमबी मुक्त मेमोरी होने पर 614 केबी आवंटित नहीं करेगा?

+0

[यह] (http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue-while-loading-an-image-to-a-bitmap-object/14731953#14731953) मदद कर सकता है! –

उत्तर

2

1.6 एमबी मेमोरी बहुत कुछ लगता है लेकिन यह मामला हो सकता है कि स्मृति इतनी बुरी तरह विभाजित हो कि यह एक ही समय में स्मृति के इतने बड़े ब्लॉक को आवंटित नहीं कर सकता है (फिर भी यह बहुत अजीब लगता है)।

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

45

[ध्यान दें कि (जैसा कि कॉमन्सवेयर नीचे बताए गए हैं) इस उत्तर में पूरा दृष्टिकोण केवल 2.3.x (जिंजरब्रेड) तक लागू होता है। हनीकॉम बिटमैप डेटा के रूप में वीएम ढेर में आवंटित किया गया है।]

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

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

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

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

देशी ढेर आकार प्लेटफ़ॉर्म द्वारा भिन्न होता है।तो स्टार्टअप पर, हम अधिकतम अनुमत वीएम ढेर आकार की जांच करने के लिए अधिकतम स्वीकार्य देशी ढेर आकार निर्धारित करते हैं। [जादू संख्या 2.1 और 2.2 पर परीक्षण द्वारा निर्धारित किया गया है, और अन्य API स्तरों पर अलग हो सकता है।]

long mMaxVmHeap  = Runtime.getRuntime().maxMemory()/1024; 
long mMaxNativeHeap = 16*1024; 
if (mMaxVmHeap == 16*1024) 
    mMaxNativeHeap = 16*1024; 
else if (mMaxVmHeap == 24*1024) 
    mMaxNativeHeap = 24*1024; 
else 
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap); 

फिर हर बार जब हम BitmapFactory कॉल करने की आवश्यकता हम प्रपत्र का चेक द्वारा कॉल पूर्व में होना।

long sizeReqd  = bitmapWidth * bitmapHeight * targetBpp/8; 
long allocNativeHeap = Debug.getNativeHeapAllocatedSize(); 
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap) 
{ 
    // Do not call BitmapFactory… 
} 

ध्यान दें कि heapPad तथ्य यह है कि एक) मूल निवासी ढेर आकार की रिपोर्टिंग "सॉफ्ट" है के लिए अनुमति देने के लिए एक जादुई संख्या है और ख) हम अन्य एप्लिकेशन के लिए स्थानीय ढेर में कुछ जगह छोड़ना चाहते हैं। हम वर्तमान में 3 * 1024 * 1024 (यानी 3 एमबाइट्स) पैड के साथ चल रहे हैं।

+0

एक बहुत ही रोचक और व्यावहारिक दृष्टिकोण। – BonanzaDriver

+0

दिलचस्प पोस्ट। आप किसी भी सबूत मिल गया है/इस एक के लिए सूत्रों का कहना है ?: "देशी ढेर अनुप्रयोगों चल बीच साझा किया जाता है, तो मुक्त स्थान की मात्रा पर क्या अन्य अनुप्रयोगों चल रहे हैं और उनके बिटमैप उपयोग निर्भर करता है।" – mibollma

+0

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

0

हालांकि आमतौर पर यह त्रुटि को पकड़ने के लिए समझ में नहीं आता है क्योंकि आमतौर पर उन्हें केवल वीएम द्वारा फेंक दिया जाता है लेकिन इस विशेष मामले में त्रुटि को जेनी गोंद कोड द्वारा फेंक दिया जाता है, इस प्रकार उन मामलों को संभालना बहुत आसान होता है जहां आप लोड नहीं कर सकते छवि: बस OutOfMemoryError पकड़ो।

0

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

ऐसा लगता है यद्यपि हार्डवेयर त्वरण आपके विचारों पर सभी प्रकार के कैशिंग करेगा, स्वयं के बिटमैप्स बनायेगा, और चूंकि सभी बिटमैप मूल ढेर साझा करते हैं, आवंटन आकार बहुत नाटकीय रूप से बढ़ सकता है।

+0

और हम यह कैसे करते हैं? ... –

+1

@ हेनरिक सोसा: एंड्रॉइड जोड़ें: AndroidAanelerated = एंड्रॉइड में आपकी गतिविधि के लिए "झूठी" AndroidManifest.xml – samgak

+0

आश्चर्यजनक रूप से इस मुद्दे को हल किया .. मुझे विश्वास नहीं है कि Google ने स्मृति के इस चौगुनी उपयोग को कितना खराब तरीके से दस्तावेज किया ! –

2

यह Torid's answer में दिए गए मुद्दों की तरह लगता है एंड्रॉयड के नवीनतम संस्करण में हल किया गया है।

हालांकि, अगर आप एक छवि कैश (एक विशेष एक या यहां तक ​​कि बस एक नियमित रूप से HashMap) का उपयोग कर रहे हैं, यह बहुत आसान एक स्मृति रिसाव बनाने के द्वारा यह त्रुटि मिलना है।

मेरे अनुभव में, यदि आप अनजाने में अपने Bitmap संदर्भ पर पकड़ और एक स्मृति रिसाव बनाने के लिए, ओपी के त्रुटि (एक BitmapFactory और देशी तरीकों के संदर्भ में) एक ही है कि अपने अनुप्रयोग (आईसीएस अप करने के लिए दुर्घटना जाएगा - 14 और +?)

इससे बचने के लिए, आपको अपने बिटमैप्स के "जाने दो" बनाएं। इसका मतलब है कि आपके कैश के अंतिम स्तर में सॉफ़्ट रेफरेंस का उपयोग करना, ताकि बिटमैप्स को कचरा मिल सके। यह काम करना चाहिए, लेकिन अगर आप अभी भी दुर्घटनाओं हो रही है, आप कोशिश कर सकते हैं स्पष्ट रूप से bitmap.recycle() का उपयोग करके संग्रह के लिए कुछ खास बिटमैप चिह्नित करने के लिए, बस अगर bitmap.isRecycled() अपने अनुप्रयोग में उपयोग के लिए एक बिटमैप वापस कभी नहीं करने के लिए याद है।

एक अलग रूप में के रूप में, LinkedHashMaps आसानी से काफ़ी अच्छा कैश संरचनाओं को लागू करने, खासकर यदि आप this example (starting line 308) में की तरह हार्ड और सॉफ्ट संदर्भ गठबंधन ... लेकिन मुश्किल संदर्भों का उपयोग के लिए एक महान उपकरण हैं कैसे आप अपने आप को स्मृति में प्राप्त कर सकते हैं भी है यदि आप गड़बड़ करते हैं तो रिसाव की स्थिति।

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