「內(nèi)存抖動(dòng)」?別再?lài)樆C嬖囌邆兞诵袉?/h1>
從「內(nèi)存抖動(dòng)」說(shuō)起
- 面試官:你做過(guò)性能優(yōu)化是吧。
- 面試者:嗯是的,在卡頓和耗電問(wèn)題上做過(guò)挺多事。
- 面試官:內(nèi)存抖動(dòng)的解決方案你有了解過(guò)嗎?
- 面試者:內(nèi)存什么?
- 面試官:內(nèi)存抖動(dòng)。沒(méi)有聽(tīng)過(guò)嗎?
- 面試者:……沒(méi)有。
- 面試官:呼(搖頭)。年輕人還是要敬畏技術(shù)啊,要持續(xù)學(xué)習(xí)啊。
- 面試者:我……嗯……
大家好,我是扔物線(xiàn)朱凱。
看起來(lái)很酷的詞總是容易吸引眼球,比如「雙親委托」,比如「責(zé)任鏈」,比如——「內(nèi)存抖動(dòng)」。吸引眼球就意味著會(huì)有更多點(diǎn)擊,而點(diǎn)擊量是內(nèi)容創(chuàng)作者最?lèi)?ài)的東西,所以這些名字很酷的詞就很自然地會(huì)受到各種技術(shù)文章的偏愛(ài),因?yàn)槟銓?xiě)這些文章可以得到更多的流量,這是無(wú)可厚非的。強(qiáng)調(diào)一下,這是無(wú)可厚非的。但有些作者對(duì)于流量的追求過(guò)于偏執(zhí),什么東西都喜歡過(guò)分地吹一下。
大家作為讀者,看文章的時(shí)候要有辨別力,不要被這些流量詞帶偏,更不要被某些流量販子們騙得去加到自己的面試題里來(lái)考驗(yàn)?zāi)愕拿嬖囌呤欠褡x過(guò)某篇文章,這是沒(méi)有價(jià)值的。
有價(jià)值,還是沒(méi)有?
啊啊?「內(nèi)存抖動(dòng)」這個(gè)概念竟然沒(méi)價(jià)值?
不是的 ,我不是說(shuō)內(nèi)存抖動(dòng)的概念沒(méi)價(jià)值。哎算了,我先說(shuō)一下什么是內(nèi)存抖動(dòng)吧:
在程序里,每創(chuàng)建一個(gè)對(duì)象,就會(huì)有一塊內(nèi)存分配給它;每分配一塊內(nèi)存,程序的可用內(nèi)存也就少一塊;當(dāng)程序被占用的內(nèi)存達(dá)到一定臨界程度,GC 也就是垃圾回收器(Garbage Collector)就會(huì)出動(dòng),來(lái)釋放掉一部分不再被使用的內(nèi)存。Android 里的 View.onDraw() 方法在每次需要重繪的時(shí)候都會(huì)被調(diào)用,這就意味著,如果你在 onDraw() 里寫(xiě)了創(chuàng)建對(duì)象的代碼,在界面頻繁刷新的時(shí)候,你就也會(huì)頻繁創(chuàng)建出一大批只被使用一次的對(duì)象,這就會(huì)導(dǎo)致內(nèi)存占用的迅速攀升;然后很快,可能就會(huì)觸發(fā) GC 的回收動(dòng)作,也就是這些被你創(chuàng)建出來(lái)的對(duì)象被 GC 回收掉。垃圾內(nèi)存太多了就被清理掉,這是 Java 的工作機(jī)制,這不是問(wèn)題。問(wèn)題在于,頻繁創(chuàng)建這些對(duì)象會(huì)造成內(nèi)存不斷地攀升,在剛回收了之后又迅速漲起來(lái),那么緊接著就是又一次的回收,對(duì)吧?這么往復(fù)下來(lái),最終導(dǎo)致一種循環(huán),一種在短時(shí)間內(nèi)反復(fù)地發(fā)生內(nèi)存增長(zhǎng)和回收的循環(huán)。
這種循環(huán)往復(fù)的狀態(tài)就像是水波紋的顫動(dòng)一樣,它的專(zhuān)業(yè)稱(chēng)呼叫做 Memory Churn,Android 的官方文檔里把它翻譯做了內(nèi)存抖動(dòng)。所以?xún)?nèi)存抖動(dòng)其實(shí)并不是我們的內(nèi)存在整體地進(jìn)行搖晃這樣神奇的事情。
而僅僅是類(lèi)似有一根攪拌棒輕輕地在內(nèi)存的邊界上進(jìn)行攪動(dòng)的樣子——其實(shí)翻譯成「內(nèi)存攪動(dòng)」好像也行哈?
我們也可以通過(guò) Android Studio 的 Memory Profiler 來(lái)更直觀(guān)地觀(guān)察到這種現(xiàn)象:
內(nèi)存的回收雖然很快,時(shí)間成本很低,但終究是有時(shí)間成本的。一兩次內(nèi)存回收不容易被用戶(hù)察覺(jué),但多次內(nèi)存回收行為集中在短時(shí)間內(nèi)爆發(fā),這就造成了比較大的界面卡頓的風(fēng)險(xiǎn)。這也是為什么 Android 在官方文檔和 Android Studio 里都建議我們盡量避免在 onDraw() 里創(chuàng)建對(duì)象。
同樣的道理,不只是在 onDraw(),在次數(shù)比較大的循環(huán)里創(chuàng)建對(duì)象,同樣會(huì)導(dǎo)致內(nèi)存抖動(dòng)。不過(guò)因?yàn)樵趯?shí)踐中,我們?cè)?onDraw() 里創(chuàng)建的對(duì)象往往是繪制相關(guān)的對(duì)象,而這些對(duì)象又經(jīng)常會(huì)包含通往系統(tǒng)下層的 Native 對(duì)象的引用,這就導(dǎo)致在 onDraw() 里創(chuàng)建對(duì)象所導(dǎo)致的內(nèi)存回收的耗時(shí)往往會(huì)更高,直白地說(shuō)就是——界面更卡頓。
另外呢內(nèi)存抖動(dòng)有時(shí)候也會(huì)抖著抖著就變成內(nèi)存溢出了,這就是更嚴(yán)重的情況,因?yàn)閮?nèi)存溢出的直接結(jié)果就是軟件崩潰。
有價(jià)值,但「內(nèi)存抖動(dòng)」這幾個(gè)字不是關(guān)鍵
所以,「內(nèi)存抖動(dòng)」這個(gè)概念有價(jià)值嗎?
當(dāng)然有價(jià)值了,因?yàn)檫@個(gè)詞幫助我們很形象地去形容了程序中的一種現(xiàn)象。對(duì)吧?增加、回收、增加、回收。
那么我在開(kāi)頭說(shuō)的「沒(méi)價(jià)值」,指的是什么呢?
我說(shuō)的不是「內(nèi)存抖動(dòng)」的概念沒(méi)價(jià)值,而是追求對(duì)這個(gè)概念本身的了解是沒(méi)價(jià)值的。確切地說(shuō)是,面試官們企圖用是否知道「內(nèi)存抖動(dòng)」這樣的詞來(lái)考察面試者是否具有高開(kāi)發(fā)水平這樣的面試策略,是沒(méi)價(jià)值的。內(nèi)存優(yōu)化是一個(gè)很復(fù)雜很細(xì)碎的話(huà)題,而在 onDraw() 中避免創(chuàng)建對(duì)象只是其中的冰山一角。而且就算在這冰山一角里,重點(diǎn)也只在于「避免內(nèi)存增長(zhǎng)」,而不是「避免內(nèi)存抖動(dòng)」。內(nèi)存抖動(dòng)只是一種具體的表面現(xiàn)象而已,而這種現(xiàn)象背后的原因,除了在 onDraw() 中創(chuàng)建對(duì)象,也很少再有其他場(chǎng)景了。那么這種場(chǎng)景限定極強(qiáng)的詞被專(zhuān)門(mén)列出來(lái)在面試題里,又有什么意義呢?
當(dāng)我們問(wèn)「內(nèi)存抖動(dòng)」,我們應(yīng)該關(guān)心什么
我不是在說(shuō)你面試的時(shí)候不應(yīng)該問(wèn)內(nèi)存抖動(dòng),而是說(shuō)你的注意力一定不能放在這個(gè)詞上。聽(tīng)沒(méi)聽(tīng)說(shuō)過(guò)內(nèi)存抖動(dòng)并不重要,知道內(nèi)存抖動(dòng)的原因和解決方案才重要。
嗯……沒(méi)聽(tīng)過(guò)內(nèi)存抖動(dòng),怎么會(huì)知道它的原因和解決方案?
他沒(méi)聽(tīng)過(guò)你可以告訴他呀!不是不能問(wèn)內(nèi)存抖動(dòng),而是如果你問(wèn)了之后對(duì)方表示沒(méi)聽(tīng)過(guò),你應(yīng)該進(jìn)一步引導(dǎo),比如你問(wèn)他:那么,Android 官方建議我們不要在 onDraw() 里創(chuàng)建對(duì)象你知道嗎?你知道為什么嗎?如果他立即回答這會(huì)導(dǎo)致頻繁觸發(fā)內(nèi)存回收,那不是證明他其實(shí)懂原理的嗎?這時(shí)候你再告訴他,這就叫內(nèi)存抖動(dòng),就行了。
而且也不要局限于這一個(gè)詞,你還可以繼續(xù)問(wèn):為什么在 onDraw() 里創(chuàng)建對(duì)象導(dǎo)致的結(jié)果是內(nèi)存抖動(dòng)而不是內(nèi)存溢出?這種對(duì)于能力的考察比對(duì)詞匯的考察重要多了。其實(shí)我也不是很確定這個(gè)問(wèn)題是不是每個(gè)喜歡問(wèn)內(nèi)存抖動(dòng)的面試官們都能回答上來(lái),但這樣問(wèn)才是考察程序員能力的最好方式。
如果你在面試時(shí)問(wèn)了內(nèi)存抖動(dòng),但在對(duì)方表示沒(méi)聽(tīng)過(guò)之后,你就不再進(jìn)行任何的引導(dǎo)而是直接給對(duì)方扣了分,那你其實(shí)相當(dāng)于在問(wèn):我這里有一個(gè)高端詞匯,您聽(tīng)過(guò)嗎?
最后
說(shuō)回到本質(zhì),學(xué)技術(shù)要學(xué)本質(zhì),但內(nèi)存抖動(dòng)并不是任何技術(shù)的本質(zhì)。其實(shí)我今天也并不是在聊內(nèi)存抖動(dòng)這個(gè)詞本身,而是想表達(dá)一種觀(guān)點(diǎn):
我們學(xué)技術(shù),應(yīng)該學(xué)得深,而且應(yīng)該足夠深,但不要被各種花里胡哨的詞嚇到,也不要被它們帶著跑,我們要有自己的知識(shí)體系,有自己的成長(zhǎng)邏輯。
我是扔物線(xiàn),我不和你比高低,我只助你成長(zhǎng)。我們下期見(jiàn)。
本文轉(zhuǎn)載自微信公眾號(hào)「扔物線(xiàn)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系扔物線(xiàn)公眾號(hào)。