[轉貼] 最近在作一款塔防遊戲,用的事surfaceview框架,因爲圖片過多,並且遊戲過程當中都須要這些圖片,因此加載成bitmap後形成OOM(out of memory)異常。下面是我一步一步找解決此問題的紀錄,再此分享,但願對之後出現此問題的開發者有所幫助。android
第一:出現問題,個人測試手機是2。2android操做系統,不會出現oom問題,可是在老闆的android4.2上卻出現了問題,由於是oom,因此我首先想到的是手動改變手機的內存大小限制。網上有些帖子說能夠經過函數設置應用的HEAP SIZE來解決這個問題,實際上是不對的。 VMRuntime.getRuntime().setMinimumHeapSize(NewSize); 堆(HEAP)是VM中佔用內存最多的部分,一般是動態分配的。堆的大小不是一成不變的,一般有一個分配機制來控制它的大小。好比初始的HEAP是4M大,當4M的空間被佔用超過75%的時候,從新分配堆爲8M大;當8M被佔用超過75%,分配堆爲16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小爲8M大。從新設置堆的大小,尤爲是壓縮,通常會涉及到內存的拷貝,因此變動堆的大小對效率有不良影響。Max Heap Size,是堆內存的上限值,Android的缺省值是16M(某些機型是24M),對於普通應用這是不能改的。函數setMinimumHeapSize其實只是改變了堆的下限值,它能夠防止過於頻繁的堆內存分配,當設置最小堆內存大小超過上限值時仍然採用堆的上限值,對於內存不足沒什麼做用。 setTargetHeapUtilization(float newTarget) 能夠設定內存利用率的百分比,當實際的利用率偏離這個百分比的時候,虛擬機會在GC的時候調整堆內存大小,讓實際佔用率向個百分比靠攏。在手機上進行了屢次測試,確實很差使,在此,我斷了改變內存限制的方法。shell
第二:查找出現問題的緣由。1,在網上搜索bitmap內存溢出,找到不少說是由於圖片大小引發的此問題。觀察個人資源文件,沒有太大的圖片,只是圖片數量過多,有將近900張,分別找出一張最大的圖片和幾張比較大的圖片,單獨測試,沒有發現問題。方法1排除。編程
2,既然圖片數量過多,突破點可能就是圖片數量問題。因而分別找了200,400,600圖片進行測試,在500左右的時候遇到錯誤,經過寶哥知道了將小圖片整合存放到一張大圖的方法,以此來減小圖片的數量,可是仔細想一想,加載成bitmap的時候仍是要切割成小圖生成bitmap,因此對此方法表示懷疑。因爲之前沒用過此方法,試試也無妨。所用到的工具是gdx—texturepackger,它只是一個工具,這裏就很少說了。測試的最終結果是仍是oom。方法2排除。app
3,如今看來,既然不是圖片數量的問題,並且會在500張左右的時候報錯,那就多是佔用內存大小的問題了,Android手機有內存限制,可是個人圖片大小又大於這個限制,這讓我頭疼了很長時間,研究國外的一些文章,從中發現了一些有用的信息,這些信息可以加深你對Android的解析bitmap機制的理解,在此分享:框架
As of Honeycomb Bitmap data is allocated in the VM heap.eclipse
做爲蜂窩位圖數據是在VM分配堆。)函數
There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library. 有一個引用在VM堆(小),但實際的數據是在本機堆分配由底層Skia圖形庫。 Unfortunately, while the definition of BitmapFactory.decode…() says that it returns null if the image data could not be decoded, the Skia implementation (or rather the JNI glue between the Java code and Skia) logs the message you’re seeing (「VM won’t let us allocate xxxx bytes」) and then throws an OutOfMemory exception with the misleading message 「bitmap size exceeds VM budget」. 不幸的是,雖然BitmapFactory.decode的定義…()表示,它返回null若是圖像數據不能解碼,Skia實現(或者說JNI膠之間的Java代碼和Skia)日誌消息你看到(「VM不會讓咱們分配xxxx字節」),而後拋出一個OutOfMemory異常與誤導信息」位圖的大小超過VM預算」。 The issue is not in the VM heap but is rather in the Native heap. 這個問題不是在VM堆而是在本機堆。 The Natïve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage. 本機堆是正在運行的應用程序之間共享,所以空閒空間的大小取決於其餘運行程序,他們使用的位圖。工具
However, I have found that getNativeHeapFreeSize() and getNativeHeapSize() are not reliable. 然而,我發現getNativeHeapFreeSize()和getNativeHeapSize()是不可靠的。測試
The Native heap size varies by platform. 本機堆大小不一樣的平臺。 So at startup, we check the maximum allowed VM heap size to determine the maximum allowed Native heap size. 因此在啓動時,咱們檢查最大容許VM堆大小來肯定最大容許本機堆大小。spa
「Bitmap data is not allocated in the VM heap」 — it is allocated on the VM heap as of Honeycomb 「位圖數據不是在VM分配堆」——這是VM分配的堆在蜂窩Yes. 是的。 As of Honeycomb (v3.0), bitmap data is allocated on the VM heap. 做爲蜂窩(v3.0),位圖數據堆上分配VM。 So all of the above only applies to Gingerbread (v2.3.x) and before 因此全部上述只適用於薑餅(v2 3 x)和以前
這些信息零零散散,可是不難發現,問題的緣由就在於根據Android版本的不一樣,bitmap data存放的位置是不一樣的,3.0之前是分配在native heap上,3.0之後是分配在VM heap上。
爲了驗證這個問題,咱們須要抓去heap快照,衆所周知,eclipse中的ddms能夠查看heap信息,可是不夠全面,這裏我用到了adb shell dumpsys meminfo+包名 這條命令來查看heap信息,對比兩個機子的不一樣以下:
2.2的
4.0
從中不難發現,bitmap的存放位置根據Android版本的不一樣真的有所不一樣。好了,下面就是找出怎麼把圖片存放到native heap裏就好了,BitmapFactory裏就那麼幾個decode方法,很容易找到BitmapFactory .decodeStream就能夠解決。下面貼一下代碼:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.ARGB_8888;
options.inPurgeable = true;// 容許可清除
options.inInputShareable = true;// 以上options的兩個屬性必須聯合使用纔會有效果
String sname = String.format( 「xxx.png」, sTowerStyle, j, sDirction, i);
InputStream is = am.open(sname);
arrBmp[ iBmpIndex] = BitmapFactory .decodeStream(is, null, options);
ok搞定手工。
小問題大發現:1.遇到問題,不要急躁。最初遇到這個問題的時候覺得很好解決,試了幾種方法後仍是解決不了,心裏不免會有挫敗感,這個時候,最須要的是耐心。
2.網上有不少資源,可是能不能查獲得就是本身的問題了,我發現那些編程老手們查找問題老是可以準肯定位,快速的找到解決方法。之後要增強這方面的鍛鍊。
3.國內的資源大多偏向解決問題,國外的資源大多偏向分析問題,因此有的時候仍是須要多看看外文的一些資料。固然這須要不錯的英文功底。當初看外文的資料,頭都大了。這是須要增強的一個方面。