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