J2ME記憶體佔用詳解及最佳化方法作者:蔣濤出處:blog責任編輯:方舟
我想做過J2ME的人,特別是像我這樣做手機遊戲的,肯定會對OutOfMemoryError這個異常深惡痛絕,尤其是在老40這樣變態的機型上,甚至對這個異常都產生了恐懼。還好我現在總算不做這個機型了,對那些仍然在為這個機型移植遊戲的同志們感到同情。為了能夠稍微緩解一下他們的痛苦,也為了廣大J2ME的從業者和愛好者能盡量減少與該異常的見面次數,CoCoMo將把自己的經驗分享一下。
首先瞭解一下分析記憶體佔用的方法,一般有兩種:模擬器內建工具和Runtime類方法。
模擬器內建工具:WTK貌似帶了一個Memory Monitor,而且許多學者人士也夸夸其談他的使用方法,但我不知道有多少人真正在用。就我對他的瞭解,首先運行他你的程式會慢的一塌糊塗,這對遊戲開發人員來說簡直是無法忍受的。但我出於研究目的仍然讓他跑了半個小時才發現原來他根本無法顯示正確的記憶體佔用量,我載入一張很大的圖片後他的記憶體線好像只出現了微微的波動又停留在原位,呵,看來的確是拿出來秀的。我一般使用的是7210模擬器內建的記憶體監視器,類比的很准,但唯一的缺點是記憶體太少,才200K。我也見某些人使用3220的模擬器監視記憶體,好像記憶體稍微大一點,我還沒來得及嘗試就再也不用為老40寫程式了,慶幸。
Runtime類方法:我經常用這個語句System.out.println(Runtime.getRuntime().freeMemory());後來整合進了我的引擎,他能夠顯示當前剩餘記憶體。不記得有多少次我用它在老40上來尋找記憶體佔用峰值。
瞭解了分析記憶體的方法,來看看記憶體佔用的罪魁禍首:程式和資源。
程式:類會被編譯成class位元組碼檔案隨MIDlet的啟動載入進記憶體,而且是一次性全部加入。也就是說MIDlet裡類個數越多、單個類程式越長、類內字串常量及資料越多,編譯後的class檔案就越大,載入後佔用的記憶體也越多。我經常在MIDlet類的建構函式裡用Runtime方法來查看MIDlet啟動後整個程式佔用記憶體量。
最佳化方法:
1.某些同志將MIDlet程式寫成兩個類來減少記憶體佔用量,但是以犧牲Java的OOP特性為代價的。在程式比較大時這種弊端將尤為顯見。而且CoCoMo曾經遇到過單個類過大,載入時間過長而違反百寶箱有關Logo 6秒時間限制的情形。因而我現在的程式加帶引擎一般都是6-7個類。
2.盡量編寫優雅的代碼,減少函數數量,在程式發布時去掉try catch,最大限度的減少程式行數,這一般都是在老40上沒有辦法的辦法,現在CoCoMo已經不靠這個來省記憶體了。
3.將資料及字串寫進檔案,在用時方載入記憶體,不用時設為null。
4.I/O操作getClass().getResourceAsStream(file);、資料庫操做RecordStore.openRecordStore(name, true);、聲音建立Manager.createPlayer();、映像建立Image.createImage(file);會在短時間內佔用大量記憶體且過後釋放,如果MIDlet程式記憶體剩餘量不足則會在這些函數頻繁調用時發生記憶體溢出,產生所謂的記憶體峰值,尤其在老40上比較普遍。當你再次與討厭的OutOfMemoryError碰面時,多用Runtime尋找記憶體峰值發生位置並盡量將這些語句分開調用,並靈活運用System.gc()來及時回收。
資源:
圖片:是佔用記憶體的大戶,尤其是手機遊戲圖片資源眾多。對圖片資源在記憶體中佔用量的計算成為J2ME遊戲開發人員的經常性工作,CoCoMo來解釋一下如何計算圖片在記憶體中的佔用量:
記憶體佔用量=寬*高*像素位元組數,其中像素位元組數因機型而異。
例如一張64*64的圖片在7210上的記憶體佔用量=64*64*1.5=6144(位元組)=6K、在S60上的記憶體佔用量=64*64*2=8192(位元組)=8K。像素位元組數因機型而異,例如7210是4096色機型,也就是說用12位來表示一個像素,所以乘上1.5,而S60是65536色的機型,用16位來表示一個像素,所以乘上2。
最佳化方法:
有些人認為壓縮圖片可以節省記憶體,這種想法是錯誤的。根據上面的解釋圖片載入記憶體後只和寬高有關係,和圖片資料量大小沒有任何關係,壓縮圖片只能減少jar大小而不能見少記憶體佔用量。
1.靜態法:減小圖片大小,寬高小了結果當然小了。根據這個思路出現了動畫編輯器之類的工具,像gameloft的波斯王子,人物被分割後使人體的部位可以重用,各部位緊湊放置都是為了較少圖片大小,充分利用圖片中的每一寸空間。
2.動態法:減少同一時刻載入記憶體的圖片數。CoCoMo曾經在火影武士項目中遇到過這種情況,當時有6種怪物,如果同時載入記憶體在老40上肯定爆掉了,但是每關只出現兩到三種怪物,所以每一關只需要載入該關出現的怪物圖片即可。現在想起來當時做這個項目在老40上溢出頻出,真把我搞死了。
聲音:聲音也是比較耗用記憶體的資源,聲音中音軌所佔的byte會轉化成位元組流被載入到記憶體中。因而減少音軌所佔byte即可減少記憶體耗用量。目前gameloft的做法是用聲音轉化工具將mid轉化為ott,然後變為ByteArrayInputStream位元組流來建立Player。