J2me性能優(yōu)化,避免內(nèi)存溢出小結(jié)
在寫程序初期由于是面向midp2.0的手機(jī)而且不是游戲的開發(fā),所以沒(méi)有對(duì)性能方面做太多的考慮,大膽的使用了很多vector數(shù)組(使用起來(lái)方便:))等等,而且程序?qū)懙囊彩请S心所欲,似乎不是在手機(jī)上開發(fā)j2me的東西。結(jié)果讓我大跌眼鏡,沒(méi)辦法只能重新優(yōu)化,這期間可想而知浪費(fèi)了很多時(shí)間,也使開發(fā)效率降低到了最低。說(shuō)到這,只有一句話奉勸大家,不管你是開發(fā)什么樣手機(jī)程序,一定要把性能放在第一位,不要重蹈我的覆轍。
我的幾個(gè)vector存放的是鏈接的內(nèi)容,文本框的內(nèi)容,圖片的內(nèi)容和普通文本的內(nèi)容,每個(gè)vector中包含的其他信息還有它所占的行數(shù),它的位置和它的標(biāo)志。我用線程控制paint(只要目的是實(shí)現(xiàn)持續(xù)按鍵的機(jī)制),這樣的話,每次重繪都要把所有內(nèi)容重新畫到屏幕上,而且還要對(duì)當(dāng)前焦點(diǎn)進(jìn)行重繪。在重繪的過(guò)程中每一次都要遍歷一遍幾乎所有的vector然后重繪,這樣肯定是浪費(fèi)資源。結(jié)果也一樣,在稍微低端一點(diǎn)手機(jī)上響應(yīng)明顯的慢。優(yōu)化這樣程序,我首先想到的就是把vector用別的方法替換掉,正常的性能使用是字符串-〉一維數(shù)組-〉二維數(shù)組-》vector。一維數(shù)組肯定是替代不了我的vector了,那只能是使用二維數(shù)組了。費(fèi)了好大勁把vector轉(zhuǎn)換位二維數(shù)組,發(fā)現(xiàn)性能上并沒(méi)有提升多少。大概只節(jié)省了20k左右的內(nèi)存,只能再想別的辦法。這里簡(jiǎn)單說(shuō)下在手機(jī)上獲得當(dāng)前內(nèi)存和剩余內(nèi)存的方式:
longtempMin=1000000;
longtemp=Runtime.getRuntime().freeMemory();
if(tempMin》temp){
tempMin=temp;
}
g.drawString(total=+Runtime.getRuntime().totalMemory(),10,10,0);
g.drawString(frees=+temp,10,10+lineHeight,0);
g.drawString(hiegh=+tempMin,10,10+2*lineHeight,0);
把這些東西畫到你的畫布上,在真機(jī)上運(yùn)行的時(shí)候方便你查看內(nèi)存的使用率。(每種機(jī)器的內(nèi)存最大值是不同的)。
而且可以查看內(nèi)存峰值。當(dāng)然你也可以使用wtk自帶的內(nèi)存和方法查看器來(lái)判斷。
既然轉(zhuǎn)換vector不能解決問(wèn)題,那就繼續(xù)優(yōu)化。內(nèi)存的主要使用看來(lái)是在每次重畫的時(shí)候產(chǎn)生的,這樣就要減少每次重畫的內(nèi)容,我采取的方式是把所有固定要重畫的東西用雙緩沖的方式畫到一張圖片上,這樣只有初始化的時(shí)候才去遍歷數(shù)組,經(jīng)過(guò)一次的遍歷把所有內(nèi)容畫到一張圖片上,然后每一次的重畫都是在重畫一張圖片。經(jīng)過(guò)這樣的處理性能上有了很大的提升。高興還為時(shí)尚早,nokia的低端機(jī)器沒(méi)有問(wèn)題了,結(jié)果在moto的機(jī)器上出了問(wèn)題,根本就不能初始化,也就是說(shuō)創(chuàng)建那張圖片的時(shí)候就應(yīng)用程序錯(cuò)誤了。繼續(xù)查找原因,結(jié)果發(fā)現(xiàn)是因?yàn)閙oto的機(jī)器不支持創(chuàng)建一張那么大圖片。也就是說(shuō)你創(chuàng)建一張大圖的時(shí)候,在moto的機(jī)器上根本就不能申請(qǐng)到內(nèi)存。找到原因后,把大圖分割為兩個(gè)比較小的圖,ok沒(méi)有問(wèn)題了。當(dāng)然至于moto支持創(chuàng)建多大的圖片可能每種機(jī)器不同吧,只能在需要的時(shí)候自己測(cè)試了。對(duì)于瀏覽器,一張分割為兩張需要做些代碼的處理。實(shí)際上為了性能的更加優(yōu)化可以把一張大圖分割為多張小圖,每張小圖的大小可以根據(jù)屏幕的幾倍大小確定也可以根據(jù)機(jī)型固定。初始化的時(shí)候也可以先初始化一部分圖。因人而異。
至此,程序的主要瓶頸已經(jīng)找到,并且解決。正應(yīng)了一句話,程序是花80%的時(shí)間在執(zhí)行20%的代碼。也就是說(shuō)我們要把主要精力放在那20%的代碼的優(yōu)化上,但實(shí)際中我發(fā)現(xiàn)對(duì)另外80%代碼的優(yōu)化也很重要,主要是一些編程細(xì)節(jié)上的處理。在細(xì)節(jié)的代碼書寫上多注意些也有利于程序性能的提升。關(guān)于細(xì)節(jié)上覺(jué)得以下幾個(gè)方面對(duì)程序性能的提升很有幫助。
首先系統(tǒng)垃圾回收的利用:關(guān)于堆內(nèi)存(heap)與棧內(nèi)存(stack)我們知道,heap存放的是對(duì)象實(shí)例與變量;而stack存放的是靜態(tài)方法。堆內(nèi)存在JVM啟動(dòng)的時(shí)候被創(chuàng)建,堆內(nèi)存中所存儲(chǔ)的對(duì)象可以被JVM自動(dòng)回收。在這里,要手動(dòng)把不用對(duì)象置為null,特別是較大的對(duì)象,如果不用一定要記得置為空。比如說(shuō)較大的數(shù)組,vector或者是image對(duì)象。(切忌)在這里,瀏覽器中頁(yè)面圖片的讀取我是采用的是后臺(tái)讀取,即先顯示文字部分,而后后臺(tái)讀取頁(yè)面中的圖片,讀取完成后再一起重新顯示。重新顯示的時(shí)候要重新構(gòu)建那個(gè)雙緩沖圖片,而我當(dāng)時(shí)就忘記了把原來(lái)創(chuàng)建的那個(gè)雙緩沖圖片置為null了,走了很多彎路才解決問(wèn)題。所以要切忌至少把大的對(duì)象置空,不要指望垃圾回收。
其次是static的使用:靜態(tài)變量在程序運(yùn)行期間內(nèi)存空間對(duì)所有該類的對(duì)象實(shí)例而言是共享的,即只在內(nèi)存中保存一份拷貝,這樣節(jié)約了不比要的內(nèi)存開銷。但是static生命周期較長(zhǎng),而且不容易被垃圾回收機(jī)制所回收,所以要合理運(yùn)用,不要適得其反。建議在全部具備下列條件的情況下盡量使用靜態(tài)變量:
1),變量所包含的對(duì)象體積較大,占用內(nèi)存較多。
2),變量所包含的對(duì)象生命周期較長(zhǎng)。
3),變量所包含的對(duì)象數(shù)據(jù)穩(wěn)定。
4),該類的對(duì)象實(shí)例有對(duì)該變量所包含的對(duì)象的共享需求。
在我的程序中對(duì)靜態(tài)變量的優(yōu)化后,使程序占用內(nèi)存量至少提升了5k-10k。所以也不容忽視。
還有就是String類相關(guān)的東西:1。字符串累加的時(shí)候一定要用StringBuffer的append方法,不要使用+操作符連接兩個(gè)字符串。差別很大。而且在循環(huán)或某些重復(fù)執(zhí)行的動(dòng)作中不要去創(chuàng)建String對(duì)象,因?yàn)镾tring對(duì)象是要用StringBuffer對(duì)象來(lái)處理的,一個(gè)String對(duì)象應(yīng)該是產(chǎn)生了3個(gè)對(duì)象(大概是這樣:))。
2,字符串length()方法來(lái)取得字符串長(zhǎng)度的時(shí)候不要把length放到循環(huán)中,可以在循環(huán)外面對(duì)其取值。(包括vector的size方法)。特別是循環(huán)次數(shù)多的時(shí)候,盡量把length放到循環(huán)外面。
intsize=xmlVector.size();
for(inti=2;i
。。。
}
在程序中我曾經(jīng)誤寫了這樣一句:if(i=5){。。。},編譯器沒(méi)有報(bào)錯(cuò),而且結(jié)果好像是把i的值改變了,沒(méi)有記清,大家有興趣可以測(cè)驗(yàn)下。書寫上盡量認(rèn)真,否則查找bug的時(shí)候可能會(huì)折騰死人的。
對(duì)代碼進(jìn)行優(yōu)化的最簡(jiǎn)單辦法就是首先不要調(diào)用這些代碼。這并不是說(shuō)要?jiǎng)h除這些代碼,也許可以用其他辦法來(lái)調(diào)用它們(后者事實(shí)減少對(duì)它的調(diào)用)。游戲的主循環(huán) 是游戲運(yùn)行的最主要做的事情,應(yīng)該更多地考慮是否可以不用或減少對(duì)屬于這個(gè)區(qū)域內(nèi)的代碼的調(diào)用。在前一片文章中介紹了方法和內(nèi)存評(píng)測(cè)工具的使用,但它們只 能幫助我們找出究竟是哪段代碼降低了程序的運(yùn)行速度,下面的內(nèi)容是參考了其他資料整理出來(lái)的優(yōu)化代碼的方法。
代碼優(yōu)化的技術(shù)大致分為兩個(gè)主要方面:高級(jí)優(yōu)化,從使用的整體算法和結(jié)構(gòu)出發(fā)進(jìn)行的優(yōu)化;低級(jí)優(yōu)化,集中于孤立的代碼片斷(通常為方法中的代碼)的優(yōu)化。下面分別討論兩方面的優(yōu)化:
一, 高級(jí)優(yōu)化
1, 感覺(jué)到就是真實(shí)
對(duì)于電影來(lái)說(shuō),我們通過(guò)攝像頭看到的都是完美的,而在拍攝現(xiàn)場(chǎng)我們看到的卻是木頭,泡沫和膠帶。所以對(duì)于電影來(lái)說(shuō),感覺(jué)到就是真實(shí)。
游戲也一樣,只需要處理游戲需要的東西。在游戲開發(fā)的各個(gè)方面這都是實(shí)用的。把精力集中在使游戲有趣和完美運(yùn)行的問(wèn)題上,始終只做需要做的而丟棄其他的部分。
2, 不要?jiǎng)?chuàng)建對(duì)象
減少對(duì)象創(chuàng)建的總數(shù)量和頻率,結(jié)果能夠大大地提高游戲的性能。還必須小心在不經(jīng)意的情況下產(chǎn)生 String對(duì)象。
例如 :graphics.drawString( 0,0,”Score:” +score );
這一句代碼會(huì)在每次被調(diào)用的時(shí)候產(chǎn)生一個(gè)新的 String對(duì)象,在這里就是每一楨畫面顯示時(shí)都會(huì)產(chǎn)生新的 String對(duì)象。因此最好是只是在分?jǐn)?shù)改變的時(shí)候才構(gòu)造這個(gè) String。
3, 繪制屏幕
通常,在對(duì)游戲完成大量的優(yōu)化工作以后,收獲的將是一個(gè)大量時(shí)間耗費(fèi)在屏幕繪圖上的游戲。這是因?yàn)橐粋€(gè)游戲的主要時(shí)耗大都集中在繪制圖像的工作上(或其他的一些基本的繪圖調(diào)用)。因此,如果一開始就可以避免繪制工作,那將是對(duì)游戲的很好的優(yōu)化。
還有就是要減少屏幕繪制,循環(huán)檢測(cè)屏幕圖像是否在某個(gè)部分發(fā)生了改變,如果沒(méi)有,就不要對(duì)那部分的屏幕進(jìn)行更新。另一個(gè)方法就是增加繪制圖像的尺寸來(lái)減少單獨(dú)的繪制調(diào)用的次數(shù)。
4, 算法
最好的,也是使用最多的高級(jí)優(yōu)化是對(duì)游戲的算法方面。
二,低級(jí)優(yōu)化
1, 提前繪制復(fù)雜圖像
我們已經(jīng)知道,使用 LCDUI繪制圖像是很慢的,因此最好是能夠避免這種繪制。其中的一個(gè)方法就是用一個(gè)預(yù)生成圖像來(lái)減少?gòu)?fù)雜圖像的繪制。進(jìn)一步來(lái)講,舉例:將所有的游戲狀態(tài)信息整合到一個(gè)面板中(得分,生命數(shù),能量值等),然后對(duì)這些信息進(jìn)行一次性同時(shí)更新。
2, 保持類和內(nèi)存之間的平衡
產(chǎn)生新的類會(huì)增加 JAR包文件的大小,因此應(yīng)該盡量避免。有的時(shí)候增加了額外類的開銷可能節(jié)省了額外的內(nèi)存開銷,這也是值得的。
3, 復(fù)雜值的預(yù)計(jì)算
節(jié)省運(yùn)算的一個(gè)好方法就是對(duì)數(shù)值進(jìn)行預(yù)運(yùn)算,從而無(wú)需再調(diào)用大開銷的計(jì)算方法。一個(gè)很好的例子就是:主窗口畫布的高度和寬度就是很好的緩存對(duì)象。例如:可以調(diào)用 getHeight方法和 getWidth方法一次,然后將它們的結(jié)果緩存起來(lái),而不是在每一次繪圖中都調(diào)用這兩種方法。
4, 使用數(shù)組
在任何時(shí)候,只要可能,都應(yīng)該使用數(shù)組而不是 Vector,因?yàn)閿?shù)組的運(yùn)行速度更快。通常面臨的唯一問(wèn)題是,如果最初分配的數(shù)組空間不夠大,將需要對(duì)數(shù)組的大小進(jìn)行擴(kuò)充。這可以做到,但它需要對(duì)整個(gè)數(shù)組進(jìn)行重建。例如:
Public final static int[ ] expandArray(int [] oldArray, int expandBy)
{
int [ ] newArray = new int [oldArray.length + expandBy];
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
Return newArray;
}
任何時(shí)候,都應(yīng)該盡量使用一維數(shù)組。訪問(wèn)二維數(shù)組變量的速度只有訪問(wèn)一維數(shù)組變量的一半。當(dāng)然,仍然可以訪問(wèn)二維數(shù)組的對(duì)象,只是需要加入一點(diǎn)點(diǎn)計(jì)算。例如,與其使用這條語(yǔ)句:
world[y][x] = 0;
不如下面這條語(yǔ)句運(yùn)行的快:
world[y*tilesWide + x] = 0;
這條語(yǔ)句通過(guò)行列的位置將數(shù)值轉(zhuǎn)換成一維值,實(shí)現(xiàn)了對(duì)數(shù)組同一元素的訪問(wèn)。
5, 不要使用數(shù)組
盡管數(shù)組的訪問(wèn)比 Vector快,但仍然比直接訪問(wèn)變量要慢,因此如果可能就應(yīng)該刪除對(duì)數(shù)組的訪問(wèn),或者為一些常用方法中的數(shù)組尋求其他能提高性能的辦法。
6, 使用快速方法
并不是所有 Java調(diào)用的方法在性能上都是相同的,方法聲明方式的不同對(duì)性能會(huì)會(huì)產(chǎn)生很多的影響??梢允褂玫淖羁斓姆椒愋褪庆o態(tài)方法,因此應(yīng)該盡可能多地將代碼置于靜態(tài)調(diào)用方法中。運(yùn)行速度僅次于靜態(tài)方法的是聲明為 final的方法。運(yùn)行最慢的兩種方法是在接口中定義的方法和用關(guān)鍵字 synchronized聲明的方法,必須盡可能地避免使用這些類型的方法。
7, 其他優(yōu)化
1)異常處理非常緩慢,不要為一半的游戲邏輯使用異常,只用它們來(lái)報(bào)告真正的錯(cuò)誤狀態(tài)。
2)使用 switch表達(dá)式比使用 if條件語(yǔ)句塊的速度要快。
3)盡可能避免使用 String對(duì)象進(jìn)行運(yùn)算,使用 StringBuffer。
4)內(nèi)嵌類的運(yùn)行很慢,盡可能避免使用。
5)在完成一個(gè)引用的使用后將它設(shè)為 null。
6)不要浪費(fèi)時(shí)間來(lái)將一個(gè)對(duì)象初始化為 null或 0, java虛擬機(jī)會(huì)替我們完成這樣的初始化
7)多思考新方法,這會(huì)使我們的大腦運(yùn)轉(zhuǎn)的更快。
8)如果可能,盡量使用 static,它們運(yùn)行都很快。它同時(shí)適用于方法和域,這條規(guī)則就是,如果它可以是靜態(tài)的,那么就把它聲明為靜態(tài)的。
9)避免類型轉(zhuǎn)換。
10)程序優(yōu)化的時(shí)候要有所取舍,要多捉摸了。大家有什么好的建議?一起來(lái)壯大吧。
共同探討: zxhwolfe@hotmail.com
附:關(guān)于j2me程序的調(diào)試
使用eclipseME+WTK2.1進(jìn)行J2ME應(yīng)用程序調(diào)試(debug)-- 引用mingjava的帖子。
選擇windows-》preferences-》java-》debug 不要選擇suspend execution的前面兩個(gè)選項(xiàng),在debuger timeout選項(xiàng)中 第一個(gè)時(shí)間至少設(shè)置為15000ms 這樣就可以調(diào)試了。
《script src=“http://partner.googleadservices.com/gampad/google_service.js” type=“text/javascript”》《/script》 《script type=“text/javascript”》《!-- try { GS_googleAddAdSenseService(“ca-pub-4210569241504288”); GS_googleEnableAllServices(); } catch (e) { } // --》《/script》 《script src=“http://partner.googleadservices.com/gampad/google_ads.js”》《/script》 《script type=“text/javascript”》《!-- try { GA_googleAddSlot(“ca-pub-4210569241504288”, “cnblogs_commentbox_up”); GA_googleAddSlot(“ca-pub-4210569241504288”, “cnblogs_blogpost_bottom”); } catch (e) { } // --》《/script》 《script type=“text/javascript”》《!-- try { GA_googleFetchAds(); } catch (e) { } // --》《/script》
評(píng)論
查看更多