1為什麼要做性能優化? 手機性能越來越好,不用糾結這些細微的性能? Android每一個應用都是運行的獨立的Dalivk虛擬機,根據不同的手機分配的可用記憶體可能只有(32M、64M等),所謂的4GB、6GB運行記憶體其實對於我們的應用不是可以任意索取 詳情:http://10.158.0.33/bbs ...
1為什麼要做性能優化? 手機性能越來越好,不用糾結這些細微的性能? Android每一個應用都是運行的獨立的Dalivk虛擬機,根據不同的手機分配的可用記憶體可能只有(32M、64M等),所謂的4GB、6GB運行記憶體其實對於我們的應用不是可以任意索取 詳情:http://10.158.0.33/bbs/forum.php?mod=viewthread&tid=122&extra= 優秀的演算法與效率低下的演算法之間的運行效率要遠遠超過電腦硬體的的發展,雖然手機單核、雙核到4核、8核的發展,但性能優化任然不可忽略 手機應用一般使用的周期比較短,用完就關了。不像伺服器應用要長年累月運行,似乎影響不大? 現在一般的用戶都不會重啟手機,可能一個月都不會重啟。像微信這樣的APP,每天都在使用。 如果一旦發生記憶體泄漏,那麼可能一點一點的累積,程式就會出現OOM。 等應用出現卡頓、發燙等,再來關註性能優化? 所以為了我們的應用的健壯性、有良好的用戶體驗。性能優化技術,需要我們用心去研究和應用。 似乎是沒錯的。現在一般我們也都是等出現問題了再來找原因。但是學好性能優化的目的不僅僅如此,我們在編碼階段就應該從源頭來杜絕一些坑,這樣的成本比後期再來尋找原因要少得多。 2
什麼是記憶體泄漏?
JVM記憶體管理 Java採用GC進行記憶體管理。深入的JVM記憶體管理知識,推薦《深入理解Java虛擬機》。 關於記憶體泄漏我們要知道,JVM記憶體分配的幾種策略。 1. 靜態的 靜態的存儲區,記憶體在程式編譯的時候就已經分配好了,這塊記憶體在程式整個運行期間都一直存在,它主要存放靜態數據、全局的static數據和一些常量。 2.棧式的 在執行方法時,方法一些內部變數的存儲都可以放在棧上面創建,方法執行結束的時候這些存儲單元就會自動被註釋掉。棧 記憶體包括分配的運算速度很快,因為內在在處理器裡面。當然容量有限,並且棧式一塊連續的記憶體區域,大小是由操作系統決定的,他先進後 出,進出完成不會產生碎片,運行效率高且穩定 3.堆式的 也叫動態記憶體 。我們通常使用new 來申請分配一個記憶體。這裡也是我們討論記憶體泄漏優化的關鍵存儲區。GC會根據記憶體的使用情況,對堆記憶體里的垃圾記憶體進行回收。 堆記憶體是一塊不連續的記憶體區域,如果頻繁地new/remove會造成大量的記憶體碎片,GC頻繁的回收,導致記憶體抖動,這也會消耗我們應用的性能 我們知道可以調用 System.gc();進行記憶體回收,但是GC不一定會執行。 面對GC的機制,我們是否無能為力?其實我們可以通過聲明一些引用標記來讓GC更好對記憶體進行回收。[td]
類型 | 回收時機 | 生命周期 |
StrongReference (強引用) | 任何時候GC是不能回收他的,哪怕記憶體不足時,系統會直接拋出異常OutOfMemoryError,也不會去回收 | 進程終止 |
SoftReference (軟引用) | 當記憶體足夠時不會回收這種引用類型的對象,只有當記憶體不夠用時才會回收 | 記憶體不足,進行GC的時候 |
WeakReference (弱引用) | GC一運行就會把給回收了 | GC後終止 |
PhantomReference (虛引用) | 如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收 | 任何時候都有可能 |
Tip:成員變數全部存儲在堆中(包括基本數據類型,引用及引用的對象實體),因為他們屬於類,類對象最終還是要被new出來的。記憶體泄漏的定義 當一個對象已經不需要使用了,本該被回收時,而有另外一個正在使用的對象持有它的引用,從而導致了對象不能被GC回收。 這種導致了本該被回收的對象不能被回收而停留在堆記憶體中,就產生了記憶體泄漏。 記憶體泄漏與記憶體溢出的區別
局部變數的基本數據類型和引用存在棧中,應用的對象實體存儲在堆中。因為它們屬於方法當中的變數,生命周期會隨著方法一起結束
- 記憶體泄漏(Memory Leak)
- 記憶體溢出(OOM)
- 應用卡頓
- 應用異常(OOM)
Android開發常見的記憶體泄漏
(1)單例造成的記憶體泄漏 錯誤示例 當調用getInstance時,如果傳入的context是Activity的context。 只要這個單例沒有被釋放,那麼這個Activity也不會被釋放一直到進程退出才會釋放。 解決方案 能使用Application的Context就不要使用Activity的Content,Application的生命周期伴隨著整個進程的周期 (2)非靜態內部類創建靜態實例造成的記憶體泄漏 錯誤示例 解決方案 將非靜態內部類修改為靜態內部類。(靜態內部類不會隱式持有外部類) (3)Handler造成的記憶體泄漏 錯誤示例 mHandler是Handler的非靜態匿名內部類的實例,所以它持有外部類Activity的引用,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息,那麼當這個Activity退出時消息隊列中還有未處理的消息或者正在處理消息,而消息隊列中的Message持有mHandler實例的引用,mHandler又持有Activity的引用,所以導致該Activity的記憶體資源無法及時回收,引發記憶體泄漏。 解決方案 創建一個靜態Handler內部類,然後對Handler持有的對象使用弱引用,這樣在回收時也可以回收Handler持有的對象,這樣雖然避免了Activity泄漏,不過Looper線程的消息隊列中還是可能會有待處理的消息,所以我們在Activity的Destroy時或者Stop時應該移除消息隊列中的消息 詳細介紹:http://blog.csdn.net/sun927/article/details/43001231 http://www.cnblogs.com/zhangming-blog/articles/6101886.html
在源碼中查看得知:用法有handler.removeCallbacksAndMessages(null);
Android HandlerThread的用法 handler.getLooper().quit();)
AsyncTask終結方式:http://blog.csdn.net/nnmmbb/article/details/53669177
//不需要用的時候記得移除監聽
(8)不需要用的監聽未移除會發生記憶體泄露 錯誤示例 解決方案 Tip:tv.setOnClickListener();//監聽執行完回收對象,不用考慮記憶體泄漏 tv.getViewTreeObserver().addOnWindowFocusChangeListene,add監聽,放到集合裡面,需要考慮記憶體泄漏 案例七和案例八總結:有註冊基本上都有取消註冊,有添加監聽就有取消監聽。