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