android優化(2)--內存泄漏之工具使用

最近找房子搬家,真是困難啊!臥槽,不知道什麼時候北京的租房的價格都變成這樣了!真是呆不下去了!

這裏寫圖片描述

好了,閒話就到這了!進入正題!

android的快速發展已有幾年了,市場也不再有前幾年那樣的火爆,並不是隨便的寫幾個findViewById,寫幾個界面就可以要十幾K工資的時期了,但是同樣的中高級工程師還是很缺的,當初那些招聘快速開發而成的中型項目,問題還是很多的,當然如果你的客戶羣體是高端用戶,手機的性能相對較好的那當我沒說!但是就目前我遇到的,很大程度上都有問題,不是代碼結構太亂,或者不怎麼規範導致的後期維護有很大的困難,就是界面的卡頓,用戶體驗性很差!那。。。你既然接盤了,就不得不做一些改進優化!真是頭大啊!!

先來解釋內存泄漏是啥!

java的GC內存回收機制:某對象不再有任何的引用的時候纔會進行回收。

內存不在GC掌控之內了。當一個對象已經不需要再使用了,本該被回收時,而有另外一個正在使用的對象持有它的引用從而就導致對象不能被回收。這種導致了本該被回收的對象不能被回收而停留在堆內存中,就產生了內存泄漏。

總之呢就是:由於其他對象的持有,被GC誤解,沒有自動回收,而實際已經無用的佔着內存的對象!也就是那些佔着廁所不拉屎的對象!

問題又來了,內存的分配策略是啥!

1.靜態的
靜態的存儲區:內存在程序編譯的時候就已經分配好,這塊的內存在程序整個運行期間都一直存在。
它主要存放靜態數據、全局的static數據和一些常量。
2.棧式的
在執行函數(方法)時,函數一些內部變量的存儲都可以放在棧上面創建,函數執行結束的時候這些存儲單元就會自動被釋放掉。
棧內存包括分配的運算速度很快,因爲內置在處理器的裏面的。當然容量有限。
3.堆式的
通常就是指在程序運行時直接 new 出來的內存,也就是對象的實例。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收。

區別:在方法體內定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內存中分配的。當在一段方法塊中定義一個變量時,Java 就會在棧中爲該變量分配內存空間,當超過該變量的作用域後,該變量也就無效了,分配給它的內存空間也將被釋放掉,該內存空間可以被重新使用

堆內存用來存放所有由 new 創建的對象(包括該對象其中的所有成員變量)和數組。在堆中分配的內存,將由 Java 垃圾回收器來自動管理。在堆中產生了一個數組或者對象後,還可以在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,這個特殊的變量就是我們上面說的引用變量。我們可以通過這個引用變量來訪問堆中的對象或者數組。

舉個例子吧

public class Sample {
    int s1 = 0;
    Sample mSample1 = new Sample();
    public void method() {
        int s2 = 1;
        Sample mSample2 = new Sample();
    }
}
Sample mSample3 = new Sample();

Sample 類的局部變量 s2 和引用變量 mSample2 都是存在於棧中,但 mSample2 指向的對象是存在於堆上的。 mSample3 指向的對象實體存放在堆上,包括這個對象的所有成員變量 s1 和 mSample1,而它自己存在於棧中。

我覺得不能再這麼一個坑一個坑的填下去了!我們得進入今天的正題了,內存工具的使用了!內存泄露的面積有點廣,各種花式泄露,如果比較感興趣可以看看這篇總結,很到位!

https://github.com/francistao/LearningNotes/blob/master/Part1/Android/Android%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%80%BB%E7%BB%93.md

當然,我們在使用工具的前提我個人覺得應該是大概知道各種泄露的發生原因,不然全靠工具來分析的話,有點不靠譜!範圍也有點大,工作量就他麼的不談了!

我們現在來寫一個簡單的單例模式的內存泄露樣例,再使用分析工具分析一下。

代碼如下:

這裏寫圖片描述

我們再在activity中進行調用:

這裏寫圖片描述

OK,看到這應該都明白,傳入activity的上下文對象,導致TestClass對MainActivity的引用,所以當我們離開MainActivity的時候,導致MainActivity不能被GC回收!我們做一個簡單的實驗,不要在清單文件中配置Activity的屬性,在它有旋轉操作的時候銷燬前一個activity再創建新的activity。我們再觀察一下monitor中Memory的情況。

旋轉之前,我們點擊一下GC讓它回收一下內存,最後內存的佔有爲14.17MB
這裏寫圖片描述

多次旋轉之後如下圖:
這裏寫圖片描述

內存的佔有爲16.27MB
我們再進行一下GC,多次GC後如圖:

這裏寫圖片描述

內存的佔有爲14.27MB,對比第一次的時候多了0.1MB,多的這部分是什麼呢?

我們再點擊GC旁邊的那個按鈕(Dump Java Heap),讓它生成分析文件:

這裏寫圖片描述

生成後的界面如下:
這裏寫圖片描述

接着我們分析一下我們自己寫的MainActivity:

這裏寫圖片描述

發現左邊的Instance的深度有兩個,事先聲明一下,我旋轉手機至少有5次,爲啥只有兩個呢?

解釋一下:現在內存中的兩個Activity分別是第一次創建時的Activity和最後展示的activity,原因,第一個呢是因爲TestClass的持有上下文對象,我們點擊GC後沒能被回收,那第二個到最後一個之前的呢,你看一下單例模式就明白了,它們未持有任何對象,而被GC無情的當垃圾回收掉了。
我們選擇第0個MainActivity,那它的關聯表就出來了:

這裏寫圖片描述

查找引用了該對象的外部對象有哪些,
然後一個一個去猜,查找可能內存泄露的嫌疑犯,依據:看(讀代碼和猜)他們的生命週期是否一致(可以通過快照對比),如果生命週期一致了肯定不是元兇。

排除一些容易被回收的(軟引用、虛引用、弱引用)

這裏寫圖片描述

當我們的項目比較的複雜的時候,那關聯樹簡直慘不忍睹啊,我們只能藉助Mat工具分析了!

接着如下操作(導出標準的.hprof文件):

這裏寫圖片描述

下載地址:

http://pan.baidu.com/s/1gfja0wF

OK,接着解壓,運行Mat ,打開我們剛纔生成的標準的hprof文件

這裏寫圖片描述

接着呢,我們就可以開始利用Mat工具進行分析了!

文檔在這:
http://pan.baidu.com/s/1nuYEMAP

相信大家看一下基本就都會用了!

好了,今天就到這了!有問題留言交流!

每天進步一點點,時間會讓你成爲巨人!加油!!

(內存泄露的參考鏈接已經在上面了!)

溜了溜了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章