1. 程式人生 > >Android App解決卡頓慢之內存抖動及內存泄漏(發現和定位)

Android App解決卡頓慢之內存抖動及內存泄漏(發現和定位)

頻率 其他 直觀 工具使用 nts and article 退出 大小

內存抖動是指在短時間內有大量的對象被創建或者被回收的現象,內存抖動出現原因主要是頻繁(很重要)在循環裏創建對象(導致大量對象在短時間內被創建,由於新對象是要占用內存空間的而且是頻繁,如果一次或者兩次在循環裏創建對象對內存影響不大,不會造成嚴重內存抖動這樣可以接受也不可避免,頻繁的話就很內存抖動很嚴重),內存抖動的影響是如果抖動很頻繁,會導致垃圾回收機制頻繁運行(短時間內產生大量對象,需要大量內存,而且還是頻繁抖動,就可能會需要回收內存以用於產生對象,垃圾回收機制就自然會頻繁運行了)。綜上就是頻繁內存抖動會導致垃圾回收頻繁運行。

內存泄漏是指某一段內存在程序裏功能上已經不需要了,但是垃圾回收機制回收內存時檢測那段內存還是被需要的,不能被回收,這種在程序中在沒有使用的但是又不能被回收的內存就是被泄漏的內存,那為什麽會這樣呢?正常的話應該是程序裏不需要的內存就可以被回收,這是垃圾回收機制做的事呀,如果垃圾回收機制正常運行的情況下,不應該這樣啊,但是實際就是垃圾回收機制正常的情況下發生的內存泄漏。其實到這裏java程序員就得知道垃圾回收機制中,判斷一段內存是否是垃圾,是否可回收的條件,這個條件是通過檢查這段內存是否存在引用和被引用關系,不存在這關系時,就認為可回收,若還存在引用或被引用關系,就認為不可回收,現在就可以知道導致內存泄漏的原因是程序員沒有將不用的內存去掉引用關系(因為程序中大多內存石油對象指向的,所以去掉引用關系就是置空)。內存泄漏會導致一些內存沒法被正常利用,話句話就是可以使用內存變少了,這樣輕則增加垃圾回收機制運行頻率,重則內存溢出(當系統需要分配一段內存,但是現有內存在垃圾回收運行後任然不足時,就會內存溢出);為避免內存泄漏,在寫程序時已經確定不需要的引用型變量,就置空;雖然即使內存沒泄露,也有可能出現內存溢出,這時的內存溢出就是有別的問題導致的。

1) Memory Churn and performance(內存抖動和性能)

雖然Android有自動管理內存的機制,但是對內存的不恰當使用仍然容易引起嚴重的性能問題。在同一幀裏面創建過多的對象是件需要特別引起註意的事情。

Android系統裏面有一個Generational Heap Memory的模型,系統會根據內存中不同的內存數據類型分別執行不同的GC操作。例如,最近剛分配的對象會放在Young Generation區域,這個區域的對象通常都是會快速被創建並且很快被銷毀回收的,同時這個區域的GC操作速度也是比Old Generation區域的GC操作速度更快的。

技術分享圖片

除了速度差異之外,執行GC操作的時候,任何線程的任何操作都會需要暫停,等待GC操作完成之後,其他操作才能夠繼續運行(所以垃圾回收運行的次數越少,對性能的影響就越少)。

技術分享圖片

通常來說,單個的GC並不會占用太多時間,但是大量不停的GC操作則會顯著占用幀間隔時間(16ms)。如果在幀間隔時間裏面做了過多的GC操作,那麽自然其他類似計算,渲染等操作的可用時間就變得少了。

導致GC頻繁執行有兩個原因:

·Memory Churn內存抖動,內存抖動是因為大量的對象被創建又在短時間內馬上被釋放。

·瞬間產生大量的對象會嚴重占用Young Generation的內存區域,當達到閥值,剩余空間不夠的時候,也會觸發GC。即使每次分配的對象占用了很少的內存,但是他們疊加在一起會增加Heap的壓力,從而觸發更多其他類型的GC。這個操作有可能會影響到幀率,並使得用戶感知到性能問題(幀率是Android渲染機制中的概念,導致卡頓慢的直接原因,就是渲染機制受阻,關於渲染機制有另一篇博客特別說了,想了解的可以點擊這裏)。

技術分享圖片

解決上面的問題有簡潔直觀方法,如果你在Memory Monitor裏面查看到短時間發生了多次內存的漲跌,這意味著很有可能發生了內存抖動。

技術分享圖片

同時我們還可以通過Allocation Tracker來查看在短時間內,同一個棧中不斷進出的相同對象。這是內存抖動的典型信號之一。

當你大致定位問題之後,接下去的問題修復也就顯得相對直接簡單了。例如,你需要避免在for循環裏面分配對象占用內存,需要嘗試把對象的創建移到循環體之外,自定義View中的onDraw方法也需要引起註意,每次屏幕發生繪制以及動畫執行過程中,onDraw方法都會被調用到,避免在onDraw方法裏面執行復雜的操作,避免創建對象。對於那些無法避免需要創建對象的情況,我們可以考慮對象池模型,通過對象池來解決頻繁創建與銷毀的問題,但是這裏需要註意結束使用之後,需要手動釋放對象池中的對象。

2) Garbage Collection in Android(Android垃圾回收)

JVM的回收機制給開發人員帶來很大的好處,不用時刻處理對象的分配與回收,可以更加專註於更加高級的代碼實現。相比起Java,C與C++等語言具備更高的執行效率,他們需要開發人員自己關註對象的分配與回收,但是在一個龐大的系統當中,還是免不了經常發生部分對象忘記回收的情況,這就是內存泄漏。

原始JVM中的GC機制在Android中得到了很大程度上的優化。Android裏面是一個三級Generation的內存模型,最近分配的對象會存放在Young Generation區域,當這個對象在這個區域停留的時間達到一定程度,它會被移動到Old Generation,最後到Permanent Generation區域。

技術分享圖片

每一個級別的內存區域都有固定的大小,此後不斷有新的對象被分配到此區域,當這些對象總的大小快達到這一級別內存區域的閥值時,會觸發GC的操作,以便騰出空間來存放其他新的對象。

技術分享圖片

前面提到過每次GC發生的時候,所有的線程都是暫停狀態的。GC所占用的時間和它是哪一個Generation也有關系,Young Generation的每次GC操作時間是最短的,Old Generation其次,Permanent Generation最長。執行時間的長短也和當前Generation中的對象數量有關,遍歷查找20000個對象比起遍歷50個對象自然是要慢很多的。

雖然Google的工程師在盡量縮短每次GC所花費的時間,但是特別註意GC引起的性能問題還是很有必要。如果不小心在最小的for循環單元裏面執行了創建對象的操作,這將很容易引起GC並導致性能問題。通過Memory Monitor我們可以查看到內存的占用情況,每一次瞬間的內存降低都是因為此時發生了GC操作,如果在短時間內發生大量的內存上漲與降低的事件(內存嚴重抖動),這說明很有可能這裏有性能問題。我們還可以通過Heap and Allocation Tracker工具來查看此時內存中分配的到底有哪些對象。

到這裏為止就簡單介紹了內存抖動(概念及判斷方法,方法是兩個工具使用,一個用於判斷有沒有嚴重內存抖動Memory Monitor,一個用於確認抖動位置Heap and Allocation Tracker),及較詳細的介紹了Android中垃圾回收機制。

2) Performance Cost of Memory Leaks(內存泄漏)

雖然Java有自動回收的機制,可是這不意味著Java中不存在內存泄漏的問題,而內存泄漏會很容易導致嚴重的性能問題。

內存泄漏指的是那些程序不再使用的對象無法被GC識別,這樣就導致這個對象一直留在內存當中,占用了寶貴的內存空間。顯然,這還使得每級Generation的內存區域可用空間變小,GC就會更容易被觸發,從而引起性能問題。

尋找內存泄漏並修復這個漏洞是件很棘手的事情,你需要對執行的代碼很熟悉,清楚的知道在特定環境下是如何運行的,然後仔細排查。例如,你想知道程序中的某個activity退出的時候,它之前所占用的內存是否有完整的釋放幹凈了?首先你需要在activity處於前臺的時候使用Heap Tool獲取一份當前狀態的內存快照,然後你需要創建一個幾乎不這麽占用內存的空白activity用來給前一個Activity進行跳轉,其次在跳轉到這個空白的activity的時候主動調用System.gc()方法來確保觸發一個GC操作。最後,如果前面這個activity的內存都有全部正確釋放,那麽在空白activity被啟動之後的內存快照中應該不會有前面那個activity中的任何對象了。

技術分享圖片

關於內存抖動和內存泄漏就到這裏了,接下來就說一下Android studio 提供的內存優化方面的工具

Android Studio提供了工具來幫助開發者發現和解決內存抖動和內存泄漏。

Tool - Memory Monitor(用於發現內存抖動及內存泄漏的)

Android Studio中的Memory Monitor可以很好的幫組我們查看程序的內存使用情況。

技術分享圖片


以下內容很重要,以下內容很重要,以下內容很重要重要的事情說三遍

·Memory Monitor:查看整個app所占用的內存,以及發生GC的時刻,短時間內發生大量的GC操作是一個危險的信號(用於發現有沒有內存泄漏和嚴重內存抖動)。

後面兩個是用於定位的內存抖動和內存泄漏發生的具體位置·

Allocation Tracker:使用此工具來追蹤內存的分配,前面有提到過。

Heap Tool:查看當前內存快照,便於對比分析哪些對象有可能是泄漏了的.

現在就可以定位到某一段代碼發生了內存泄漏或抖動。

如果是內存泄漏解決方法就很直接,在適當的時候把泄漏的對象置空就可以了。

但是如果只內存抖動的話就得分兩種情況,由於內存抖動是在短時間內創建釋放大量對象導致的(一般是循環內創建對象),直接辦法就是不再短時間內創建大量對象,如果創建對象的過程可以拿到循環外而不影響功能,這種情況比較容易解決。但是更多的是另一種情況,就是不能拿到循環外,否則影響功能。對於第二種情況就要做到再循環內創建對象,但是又要控制對象個數。這種情況還是下一篇在說明解決辦法。

.

Android App解決卡頓慢之內存抖動及內存泄漏(發現和定位)