Laya WeakMap 弱引用緩存

一、ES6 WeakMap

參考
JavaScript 內(nèi)存泄漏教程
ES6詳解四: WeakMap

及時清除引用非常重要。但是,你不可能記得那么多刽脖,有時候一疏忽就忘了,所以才有那么多內(nèi)存泄漏忌愚。最好能有一種方法曲管,在新建引用的時候就聲明,哪些引用必須手動清除硕糊,哪些引用可以忽略不計院水,當其他引用消失以后,垃圾回收機制就可以釋放內(nèi)存简十。這樣就能大大減輕程序員的負擔檬某,你只要清除主要引用就可以了。
ES6 考慮到了這一點螟蝙,推出了兩種新的數(shù)據(jù)結(jié)構(gòu):WeakSet 和WeakMap恢恼。它們對于值的引用都是不計入垃圾回收機制的,所以名字里面才會有一個"Weak"胰默,表示這是弱引用场斑。(Map的一個最大弊端就是它會導(dǎo)致作為key的對象增加一個引用,因此導(dǎo)致GC無法回收這個對象初坠,如果大量使用object作為Map的key會導(dǎo)致大量的內(nèi)存泄露和簸。)
下面代碼中,只要外部的引用消失碟刺,WeakMap 內(nèi)部的引用锁保,就會自動被垃圾回收清除。由此可見,有了它的幫助爽柒,解決內(nèi)存泄漏就會簡單很多吴菠。

// 手動執(zhí)行一次垃圾回收,保證獲取的內(nèi)存使用狀態(tài)準確
> global.gc(); 
undefined

// 查看內(nèi)存占用的初始狀態(tài)浩村,heapUsed 為 4M 左右
> process.memoryUsage(); 
{ rss: 21106688,
  heapTotal: 7376896,
  heapUsed: 4153936,
  external: 9059 }

> let wm = new WeakMap();
undefined

> let b = new Object();
undefined

> global.gc();
undefined

// 此時做葵,heapUsed 仍然為 4M 左右
> process.memoryUsage(); 
{ rss: 20537344,
  heapTotal: 9474048,
  heapUsed: 3967272,
  external: 8993 }

// 在 WeakMap 中添加一個鍵值對,
// 鍵名為對象 b心墅,鍵值為一個 5*1024*1024 的數(shù)組  
> wm.set(b, new Array(5*1024*1024));
WeakMap {}

// 手動執(zhí)行一次垃圾回收
> global.gc();
undefined

// 此時酿矢,heapUsed 為 45M 左右
> process.memoryUsage(); 
{ rss: 62652416,
  heapTotal: 51437568,
  heapUsed: 45911664,
  external: 8951 }

// 解除對象 b 的引用  
> b = null;
null

// 再次執(zhí)行垃圾回收
> global.gc();
undefined

// 解除 b 的引用以后,heapUsed 變回 4M 左右
// 說明 WeakMap 中的那個長度為 5*1024*1024 的數(shù)組被銷毀了
> process.memoryUsage(); 
{ rss: 20639744,
  heapTotal: 8425472,
  heapUsed: 3979792,
  external: 8956 }
二怎燥、Laya中緩存的需求

以Button為例瘫筐,設(shè)置的skin圖片很多是有UP,OVER,DOWN三態(tài)的,也就是在顯示時铐姚,要根據(jù)stateNum去切割Texture策肝。那么一個按鈕在UP,OVER,DOWN狀態(tài)切換時,是不可能在切換時才做切割的隐绵,一定是初始化skin時就切割好了之众。參考Button.as

public function set skin(value:String):void {
    if (_skin != value) {
        _skin = value;
        callLater(changeClips);
        _setStateChanged();
    }
}

/**
 * @private
 * 對象的資源切片發(fā)生改變。
 */
protected function changeClips():void {
    var img:Texture = Loader.getRes(_skin);
    if (!img) {
        trace("lose skin", _skin);
        return;
    }
    var width:Number = img.sourceWidth;
    var height:Number = img.sourceHeight / _stateNum;
    img.$_GID || (img.$_GID = Utils.getGID());
    var key:String = img.$_GID +"-"+ _stateNum;
    var clips:Array = WeakObject.I.get(key);
    if (clips) _sources = clips;
    else {
        _sources = [];
        if (_stateNum === 1) {
            _sources.push(img);
        } else {
            for (var i:int = 0; i < _stateNum; i++) {
                _sources.push(Texture.createFromTexture(img, 0, height * i, width, height));
            }
        }
        WeakObject.I.set(key, _sources);
    }
    ...
    
/**
 * @private
 * 改變對象的狀態(tài)依许。
 */
protected function changeState():void {
    _stateChanged = false;
    runCallLater(changeClips);
    var index:int = _state < _stateNum ? _state : _stateNum - 1;
    _sources && (_bitmap.source = _sources[index]);
    ...
    
/**@inheritDoc */
override public function destroy(destroyChild:Boolean = true):void {
    super.destroy(destroyChild);
    _bitmap && _bitmap.destroy();
    _text && _text.destroy(destroyChild);
    _bitmap = null;
    _text = null;
    _clickHandler = null;
    _labelColors = _sources = _strokeColors = null;
}

在changeClips方法中棺禾,可以看到使用WeakObject緩存了這些切割后的Texture。那么問題就是峭跳,_sources已經(jīng)在Button中對切片做了緩存帘睦,為什么還要緩存到另外一個類WeakObject中呢?
我覺得原因是這樣的坦康,如果一個或多個UI中出現(xiàn)了許多個同樣的Button,那么_sources在每一個Button實例中做緩存是不夠的诡延,肯定是要統(tǒng)一緩存到一個地方滞欠,那就是WeakObject了。然后在每一個Button實例中肆良,在destory方法中都把_sources置為null清除了對切片的引用筛璧,如果所有的Button實例都destory了,那么WeakObject中的切片緩存就會自動被GC惹恃。而普通的Map或者說Object是做不到這一點的夭谤,它無法知道自己這個緩存還有沒有Button在使用。從WeakObject.as中可以看出這一點:

public static function __init__():void {
    supportWeakMap = Browser.window.WeakMap != null;
    //如果不支持巫糙,10分鐘回收一次
    if (!supportWeakMap) Laya.timer.loop(delInterval, null, clearCache);
}

/**清理緩存朗儒,回收內(nèi)存*/
public static function clearCache():void {
    for (var i:int = 0, n:int = _maps.length; i < n; i++) {
        var obj:WeakObject = _maps[i];
        obj._obj = {};
    }
}
三、應(yīng)用場景

官方引擎中,除了Button醉锄,Clip和AutoBitmap也使用了WeakObject乏悄。可以看出恳不,當多個UI需要緩存一些共用的對象時檩小,并且只想各自destroy,不想管緩存的內(nèi)存回收問題時烟勋,就可以使用WeakObject了规求。這個和Pool是不同的,Pool是對象池卵惦,主要是減少多余的對象創(chuàng)建阻肿,池子取空后,仍然是要new出新實例來返回鸵荠。而WeakObject緩存的只有一份數(shù)據(jù)冕茅,只要key相同,所有實例用的都是那個緩存蛹找。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姨伤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子庸疾,更是在濱河造成了極大的恐慌乍楚,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件届慈,死亡現(xiàn)場離奇詭異徒溪,居然都是意外死亡,警方通過查閱死者的電腦和手機金顿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門臊泌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揍拆,你說我怎么就攤上這事渠概。” “怎么了嫂拴?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵播揪,是天一觀的道長。 經(jīng)常有香客問我筒狠,道長猪狈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任辩恼,我火速辦了婚禮雇庙,結(jié)果婚禮上谓形,老公的妹妹穿的比我還像新娘。我一直安慰自己状共,他們只是感情好套耕,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峡继,像睡著了一般冯袍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碾牌,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天康愤,我揣著相機與錄音,去河邊找鬼舶吗。 笑死征冷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的誓琼。 我是一名探鬼主播检激,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腹侣!你這毒婦竟也來了叔收?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤傲隶,失蹤者是張志新(化名)和其女友劉穎饺律,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跺株,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡复濒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乒省。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巧颈。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袖扛,靈堂內(nèi)的尸體忽然破棺而出洛二,到底是詐尸還是另有隱情,我是刑警寧澤攻锰,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站妓雾,受9級特大地震影響娶吞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜械姻,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一妒蛇、第九天 我趴在偏房一處隱蔽的房頂上張望机断。 院中可真熱鬧,春花似錦绣夺、人聲如沸吏奸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奋蔚。三九已至,卻和暖如春烈钞,著一層夾襖步出監(jiān)牢的瞬間泊碑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工毯欣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馒过,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓酗钞,卻偏偏與公主長得像腹忽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子砚作,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Java GarbageCollection(GC) Java不能像C/C++那樣直接對內(nèi)存進行操作(內(nèi)存分配和垃...
    獅_子歌歌閱讀 2,388評論 0 3
  • 1.Set 基本用法 ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set窘奏。它類似于數(shù)組,但是成員的值都是唯一的偎巢,沒有重復(fù)的值蔼夜。Set本...
    雨飛飛雨閱讀 1,854評論 0 7
  • 內(nèi)存泄露是每個開發(fā)者最終都不得不面對的問題。即便使用自動內(nèi)存管理的語言压昼,你還是會碰到一些內(nèi)存泄漏的情況求冷。內(nèi)存泄露會...
    他在發(fā)呆閱讀 604評論 0 0
  • 一直到最后一刻都沒有思索出來當下最有成就的三件事但金。 也許是自我認知的嚴重偏差韭山,把成就事件和自己的能力割裂開來說。 ...
    loulou277閱讀 663評論 1 49
  • 趙麗華曾寫到: “毫無疑問 我做的餡餅 是全天下 最好吃的” 于是梨花體一舉揚名 我曾陰險地認為 世人之所以看不上...
    waxcup閱讀 303評論 0 0