【引自CoCoMo的博客】我想做過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。