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