Flash Player 9中的資源管理策略
原文
Resource management strategies in Flash Player 9
翻譯
Actionscript 3.0帶給Flash開(kāi)發(fā)人員更加快的代碼執(zhí)行和許多新的API增強(qiáng),站在開(kāi)發(fā)人員的立足點(diǎn)來(lái)說(shuō),相對(duì)之前的版本這些改變需要更高級(jí)別的可靠性。本文重點(diǎn)討論Actionscript 3.0中新資源管理特性,以及粗略的討論下Actionscript 3.0中那些可以幫助您跟蹤和更加有效的管理內(nèi)存的工具。
Actionscript 3.0中影響資源管理的最大的改變是其新的顯示列表模型。在Flash Player 8及之前版本中,當(dāng)一個(gè)顯示(display)對(duì)象被從屏幕被移除時(shí)(使用removeMovie 或 unloadMovie),該顯示對(duì)象及其子對(duì)象將被立即從內(nèi)存總移除并且代碼即可終止,F(xiàn)lash Player 9帶來(lái)了更加靈活的顯示列表模型,在該模型中,將顯示對(duì)象(sprites、movie clips等)作為普通對(duì)象一樣對(duì)待。
這意味著開(kāi)發(fā)人員現(xiàn)在可以做一些真正酷的事情,比如重排根目錄(reparenting:將顯示對(duì)象從一個(gè)顯示列表移到另一中)和從已經(jīng)載入的SWF中實(shí)例化顯示對(duì)象。不幸的是,它也同時(shí)意味著現(xiàn)在顯示對(duì)象與其他別的對(duì)象一樣被垃圾收集器同等對(duì)待,它帶來(lái)了大量有意思(可能不明顯)的問(wèn)題。
為什么資源管理是個(gè)問(wèn)題
Flash開(kāi)發(fā)人員看了Actionscript 3.0中這些新的資源管理考慮可能覺(jué)得概念很復(fù)雜,另一方面,Java開(kāi)發(fā)人員可能覺(jué)得沒(méi)什么。這些差距是可以理解的:Flash開(kāi)發(fā)人員不習(xí)慣在基本的最佳的實(shí)踐之外實(shí)現(xiàn)手工資源管理——如當(dāng)不再使用時(shí)刪除引用——反之Java開(kāi)發(fā)人員之前就將其貫穿所有中了。這些問(wèn)題對(duì)于大多數(shù)現(xiàn)代的內(nèi)存管理語(yǔ)言也同樣會(huì)出現(xiàn),不幸的是,現(xiàn)在沒(méi)有完全避免它們的方法。
盡管資源管理是生活之本,F(xiàn)lash遇見(jiàn)了很多在其他語(yǔ)言中罕見(jiàn)的挑戰(zhàn)(包括Flex)。Flash內(nèi)容往往包含許多閑置或易交互的執(zhí)行代碼——不像Java和Flex他們大多數(shù)是交互式的。這意味著只有用戶交互時(shí)才執(zhí)行CPU密集代碼。另外,F(xiàn)lash工程比其他平臺(tái)更頻繁的從第三方資源載入(可能使用貧編碼標(biāo)準(zhǔn))外部?jī)?nèi)容。Flash開(kāi)發(fā)人員也只有更少的工具、剖析器和框架可以使用。
最后,F(xiàn)lash開(kāi)發(fā)人員通常有著很少的非正式的編程工作背景。我所知道的大部分Flash開(kāi)發(fā)人員有著音樂(lè)、藝術(shù)、商業(yè)、哲學(xué)或只是除編程外的任何背景。這種多元化的結(jié)果帶來(lái)另人震撼的創(chuàng)意和內(nèi)容,但是該社區(qū)卻沒(méi)有真正準(zhǔn)備好處理資源管理問(wèn)題。
問(wèn)題1:動(dòng)態(tài)內(nèi)容
在資源管理中遇到的其中一個(gè)明顯的問(wèn)題是與sprites(或其他顯示對(duì)象)有關(guān),您動(dòng)態(tài)實(shí)例化它們,然后希望在以后的時(shí)間里面移除它們。當(dāng)您將顯示對(duì)象從場(chǎng)景中移除后,因?yàn)樗鼈儾辉倩顒?dòng)并且掛死在顯示列表中,它仍會(huì)一直存在內(nèi)存中。如果您做好了清除它的所有引用的工作,那們?cè)谙麓卫占鬟\(yùn)行收集時(shí),該剪接將會(huì)被從內(nèi)存中移除。基于松散內(nèi)存管理使用的特性,這將有很多不確定性。
注意到這一點(diǎn)非常的重要,顯示對(duì)象不只一直占用內(nèi)存,它還會(huì)一直執(zhí)行“空閑”代碼,如定時(shí)器、進(jìn)入幀以及監(jiān)聽(tīng)是否從某一范圍出來(lái)的監(jiān)聽(tīng)器。
以下幾個(gè)示例可以幫助您說(shuō)明該問(wèn)題:
某一游戲sprite監(jiān)聽(tīng)它自己的enterFrame事件,每次幀移動(dòng)時(shí),該應(yīng)用就會(huì)處理某些計(jì)算以決定它是否接近其他游戲元素。在Actionscript 3.0中,即使您已經(jīng)將該sprite從顯示列表中移除并將所有對(duì)它的應(yīng)用都置為null,除非它已經(jīng)被垃圾收集器給移除后,在每個(gè)幀移動(dòng)時(shí),該應(yīng)用仍會(huì)一直運(yùn)行該代碼。您必須記住要當(dāng)該sprite并移除時(shí)必須明確將enterFrame監(jiān)聽(tīng)器移除。
考慮一個(gè)通過(guò)注冊(cè)場(chǎng)景的mouseMove來(lái)跟隨鼠標(biāo)的電影片段(movie clip)——在新的事件模型下這是達(dá)到該效果的唯一方式。除非您記得移除監(jiān)聽(tīng)器,每次鼠標(biāo)移動(dòng)時(shí),該片段將一直會(huì)執(zhí)行該代碼,即使在片段被“刪除”后。缺省的,因?yàn)閳?chǎng)景中為了事件發(fā)布有一個(gè)指向它的引用,該片段一直會(huì)執(zhí)行。我將在后面的文章中討論如何避免這樣的問(wèn)題
現(xiàn)在想象一下以上示例蘊(yùn)含的含義,在垃圾收集器回收前移除多個(gè)sprites——或者如果移除某一sprites所有的引用失敗會(huì)發(fā)生什么。您很容易一不小心就超出CPU最大處理能力,進(jìn)而使得您的應(yīng)用或游戲慢得像在爬,甚至搞得用戶計(jì)算機(jī)完全停頓。當(dāng)前還沒(méi)有辦法強(qiáng)行讓Flash Player殺死一個(gè)顯示對(duì)象并停止它的執(zhí)行。至多只能在該對(duì)象被從顯示中移除時(shí)由Flash開(kāi)發(fā)人員在手動(dòng)這樣做。
問(wèn)題2:已載入的內(nèi)容
記住現(xiàn)在已載入的SWF的內(nèi)容也是和其他別的對(duì)象一樣被同等對(duì)待的,并且您可以開(kāi)始想象下您在載入內(nèi)容時(shí)可能會(huì)遭遇到的某些問(wèn)題。類似其他顯示對(duì)象,當(dāng)前沒(méi)有方法可以直接將已載入SWF和它的內(nèi)容從內(nèi)容中移除。調(diào)用Loader.unload只是簡(jiǎn)單將載入器指向SWF的應(yīng)用置空;它將繼續(xù)存在并保持執(zhí)行直到它被下次垃圾收集器回收掉(確保所有對(duì)已載入內(nèi)容的其他引用都已被完整的清除)。
考慮以下兩個(gè)場(chǎng)景:
您創(chuàng)建一個(gè)用來(lái)載入您的實(shí)驗(yàn)Flash工程的shell。這一實(shí)驗(yàn)工作是尖端技術(shù),并把CPU的資源已經(jīng)用到了極限 。某一用戶點(diǎn)擊某一按鈕來(lái)載入一個(gè)實(shí)驗(yàn),查看它,然后再點(diǎn)擊某一按鈕來(lái)載入第二個(gè)實(shí)驗(yàn),如果到第一個(gè)實(shí)驗(yàn)的所有引用都已經(jīng)被清除,它將繼續(xù)在后臺(tái)運(yùn)行,當(dāng)?shù)诙€(gè)實(shí)驗(yàn)在同一時(shí)刻運(yùn)行時(shí),很可能會(huì)出現(xiàn)最大處理能力溢出。
某一客戶委托您創(chuàng)建一個(gè)應(yīng)用來(lái)載入其他開(kāi)發(fā)人員創(chuàng)建的Actionscript 3.0 SWFs。該開(kāi)發(fā)人員增加了到場(chǎng)景的監(jiān)聽(tīng)器或其他別的如創(chuàng)建了一個(gè)到其自己內(nèi)容的外部引用,它將內(nèi)存中活動(dòng)并且繼續(xù)消耗CPU資源直到用戶退出您的應(yīng)用。就算該載入的內(nèi)容沒(méi)有任何外部引用,它仍然會(huì)繼續(xù)無(wú)限期的執(zhí)行直到被下次垃圾收集器回收。
當(dāng)您設(shè)計(jì)一個(gè)載入不可信內(nèi)容的應(yīng)用時(shí),覺(jué)察到這非常重要——在您卸載它后該代碼仍會(huì)繼續(xù)執(zhí)行。雖然該內(nèi)容會(huì)遵循Flash Player安全模型規(guī)范運(yùn)行,但在您的應(yīng)用開(kāi)發(fā)過(guò)程中考慮一些潛在的漏洞仍然是一個(gè)好主意。
使用System.totalMemory
盡管System.totalMemory是一個(gè)簡(jiǎn)單工具,但它是重要的因?yàn)樵贔lash中它是開(kāi)發(fā)人員可以使用的第一個(gè)運(yùn)行時(shí)剖析工具。它可以讓您監(jiān)控Flash Player運(yùn)行時(shí)用了多少內(nèi)存。這使得您在開(kāi)發(fā)中有一定的能力調(diào)整您自己工作而不用使用系統(tǒng)監(jiān)視器。更重要的,它使得您可以在給用戶帶來(lái)一系列問(wèn)題前超前的處理您的內(nèi)容中的重大內(nèi)存泄露成為可能。釋出一個(gè)錯(cuò)誤然后終止您的應(yīng)用總是好于使用戶系統(tǒng)停頓或者甚至完全的死機(jī)。
這是一個(gè)您如何可以做到這的簡(jiǎn)單示例:
import flash.system.System;
import flash.net.navigateToURL;
import flash.net.URLRequest;
...
// check our memory every 1 second:
// 每秒檢查一下我們的內(nèi)存:
var checkMemoryIntervalID:uint = setInterval(checkMemoryUsage,1000);
...
var showWarning:Boolean = true;
var warningMemory:uint = 1000*1000*500;
var abortMemory:uint = 1000*1000*625;
...
function checkMemoryUsage() {
if (System.totalMemory > warningMemory && showWarning) {
// show an error to the user warning them that we're running out of memory and might quit
// 向用戶顯示一個(gè)錯(cuò)誤警告他們我們內(nèi)存溢出并且可能要退出了
// try to free up memory if possible
// 如果可能的話試圖釋放內(nèi)存
showWarning = false; // so we don't show an error every second
} else if (System.totalMemory > abortMemory) {
// save current user data to an LSO for recovery later?
// 將用戶數(shù)據(jù)保存到LSO中以在以后恢復(fù)?
abort();
}
}
function abort() {
// send the user to a page explaining what happpened:
// 發(fā)送給用戶一個(gè)頁(yè)面解釋發(fā)生了什么:
navigateToURL(new URLRequest("memoryError.html"));
}
很明顯以上代碼還有很多方式可以增強(qiáng),但是希望該代碼能夠演示該處理背后的基本概念。
注意到總內(nèi)存(totalMemory)是單一進(jìn)程中的共享值是重要的。一個(gè)單一進(jìn)程可能只是一個(gè)瀏覽器窗口,或者所有打開(kāi)的瀏覽器窗口,視乎于瀏覽器、操作系統(tǒng)以及該窗口是符合打開(kāi)的。例如,在Mac OS X中,所有的Safari瀏覽器窗口共享一個(gè)單一的進(jìn)程和總內(nèi)存(totalMemory)值。而在Microsoft Windows中進(jìn)程數(shù)和占用內(nèi)存值就更加的費(fèi)解。
弱引用
在Actionscript 3.0 中其中一個(gè)我真的很高興看到的特性是弱引用的實(shí)現(xiàn)。它可以描述為不被垃圾收集器計(jì)算以決定某一對(duì)象是否可以被收集的對(duì)對(duì)象的引用。如果某一對(duì)象剩余的唯一的引用是弱引用的話,那們?cè)搶?duì)象將在垃圾收集器的下次運(yùn)行時(shí)被移除。
不幸的是,弱引用只在兩種情況下支持。第一是事件監(jiān)聽(tīng)器——這太偉大了因?yàn)槭录O(jiān)聽(tīng)器是導(dǎo)致垃圾收集問(wèn)題的最常見(jiàn)引用之一。我強(qiáng)烈的推薦您總是在監(jiān)聽(tīng)器上使用弱引用。要做到這點(diǎn),要在調(diào)用addEventListener時(shí)給第五個(gè)參數(shù)傳遞true,如下所示:
someObj.addEventListener("eventName",listenerFunction,useCapture,priority,weakReference);
stage.addEventListener(Event.CLICK,handleClick,false,0,true);
// the reference back to handleClick (and this object) will be weak.
// 到 handleClick(和該對(duì)象)的引用將是弱的。
關(guān)于本章節(jié)要更多了解,請(qǐng)閱讀我blog上的關(guān)于弱引用監(jiān)聽(tīng)的文章。
Actionscript 3.0還在字典對(duì)象(Dictionary object)中支持弱引用。只需要在實(shí)例一個(gè)新的字典事向第一個(gè)參數(shù)傳遞true即可讓它使用弱引用作為它的關(guān)鍵字。如下所示:
var dict:Dictionary = new Dictionary(true);
dict[myObj] = myOtherObj;
// the reference to myObj is weak, the reference to myOtherObj is strong
// 指向myObj的引用是弱的,指向myOtherObj的引用是強(qiáng)的。
從這里通往何方
資源管理是Actionscript 3.0 開(kāi)發(fā)的重要部分。忽略本文中描述的問(wèn)題可能的結(jié)果就是遲緩的內(nèi)容(應(yīng)用),此外,也有潛在的完全拖垮用戶系統(tǒng)的風(fēng)險(xiǎn)。現(xiàn)在再也沒(méi)有任何方法可以直接將顯示對(duì)象從內(nèi)存中移除并且停止它的代碼執(zhí)行——這意味著在某一應(yīng)用中Flash開(kāi)發(fā)人員有責(zé)任在對(duì)象不再需要使用的時(shí)候?qū)⑵渫耆那謇硗桩?dāng)。
雖然Actionscript 3.0實(shí)質(zhì)上提高了開(kāi)發(fā)人員在他們應(yīng)用中管理資源必須做的工作量,但是在Flash Player 9提供了新的工具來(lái)幫助管理內(nèi)存的使用。將這些新工具和有效的策略和方法配對(duì)起來(lái)(關(guān)于該主題,請(qǐng)查看本文的姊妹篇:理解Flash Player 9垃圾收集)可以使得您在即將到來(lái)的Flash和Flex工程中成功的管理資源。
更多信息,請(qǐng)務(wù)必訪問(wèn)Flash開(kāi)發(fā)人員中心和Flash Player開(kāi)發(fā)人員中心。
posted on 2008-08-07 20:21 vinny 閱讀(293) 評(píng)論(0) 編輯 收藏 所屬分類: Flex