性能優化系統學習(一):基礎知識

前言

  不管我們是做什麼開發的 我相信性能這塊都是非常關鍵的 尤其是做app更是重中之重 可以說直接決定用戶的留存,間接的決定app的生死 並且性能優化也是檢驗一個程序員水平的重要指標 比如面試的時候就會問你之前做項目的時候有沒有做過性能優化?或者說有沒有遇到過內存泄漏 是怎麼處理的? 回答這個問題基本上就可以看出來你是不是一個老司機。這也是初級開發者進階中高級必須掌握的技能之一!下面就讓我們一起系統的學習下這塊相關的知識~

內存溢出和內存泄漏

  首先我們應該知道的是 一個app進程分配的內存 一般來講是16M、32M、64M 甚至早期的時候只有8M 也就是說給你玩的空間就這麼大 想象一下 8M?20多張大圖就崩了吧。所以我們不得不做一些措施,其中很重要的一個是什麼呢?就是內存分配和釋放。寫過C的朋友應該很熟悉,每次我們都需要手動去管理這些內存,這要求我們很細心,一不留神可能內存就泄漏了。而Android他採用的是java語言開發的,java有別於C的一個重要東西就是他有一個智能系統(基於一套複雜的算法)由他來幫助我們管理內存,他就是GC,俗稱垃圾回收機制!開發人員不用考慮內存的管理,簡化了開發,降低了出錯的可能性,而指針也被引用代替。一切OOP,萬物皆對象。用代碼將夢想照進現實..扯遠了..

  • 內存溢出(out of memory):是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory。比方說只有8M內存空間,你一下就加載了10M的東西,就溢出了.
  • 內存泄漏( memory leak):是指程序在申請內存後,由於疏忽或錯誤造成程序未能釋放已經不再使用的內存。這個什麼意思呢?比方說你聲明瞭Context,當Activity銷燬後 這個Context被應該被釋放掉的,結果沒有,它還是佔據內存空間的,而你又不能操作它,這就造成內存泄漏了,注意這會app還是可以正常運行的,但是當這樣的情況多了,有大量的內存泄漏出現,最終就會導致out of memory,程序崩潰。這就是有時候不注意,一個很小的問題就可能最終引發一個系統的癱瘓,而當着個系統很龐大的時候,你要在找到出問題的地方是很困難的,正所謂千里之堤毀於蟻穴,這就更要求我們開發人員平時寫代碼的時候要額外注意內存泄漏這塊,防患於未然。

說了這麼多,下面舉個錯誤的代碼示例:

public class CommUtil {
    private static CommUtil instance;
    private Context context;
    private CommUtil(Context context){
        this.context = context;
    }

    public static CommUtil getInstance(Context context){
        if(instance == null){
            instance = new CommUtil(context);
        }
        return instance;
    }
}

  分析:上面是一個簡單的單例類,它類部持有一個Context的引用 這樣的寫法容易造成內存泄漏
Activity中使用這個類並且把Context傳進來,當Activity被銷燬時 由於這個類對象是靜態的 所以他持有的Context無法被釋放,內存裏面就會多一個失去控制的Context 造成內存泄漏,而當其他的Activity使用這個CommUtil 類時 裏面的Context對象一直都是第一個Activity傳進來的那個,造成數據混亂。
正確的做法是什麼呢?應該是使用Application的context 它的生命週期是伴隨整個app進程的 當他被銷燬 整個app進程結束 CommUtil類也會被銷燬 自然就不會產生Context泄漏

內存分配的幾種策略

  這裏講的是一個比較籠統的概念,關於java內存分配深入研究內容還是很多很細的,需要我們搜索或者看書更全面的學習

  • 靜態存儲區:內存在程序編譯的時候就已經分配好,這塊的內存在程序整個運行期間都一直存在;它主要存放靜態數據、全局的static數據和一些常量
  • 棧:一塊連續的內存區域,在執行函數(方法)時,函數一些內部變量的存儲都可以放在棧上面創建,函數執行結束的時候這些存儲單元就會自動被釋放掉。
  • 堆:不連續的內存區,動態內存分配。可以用malloc或者new來申請分配一個內存。


  1. java裏面棧和堆分別存放的什麼?
    常見的錯誤說法:基本數據類型存在棧上,引用類型都存在堆上;引用都存在棧(不嚴謹)
    正確解釋:
    成員變量全部存儲在堆中(包括基本數據類型,引用及引用的對象實體)
    局部變量的基本數據類型和引用存儲於棧當中,引用的對象實體存儲在堆中;
  2. 棧和堆的區別是什麼?
    棧:一塊連續的內存區域,內置在處理器的裏面的,容量有限,運算速度很快。先進後出,進出完全不會產生碎片,運行效率高且穩定。
    堆:不連續的內存區域,堆空間比較靈活也特別大。管理很麻煩,頻繁地new/remove會造成大量的內存碎片,慢慢導致效率低下。

java的四種引用類型

  • 強引用(StrongReference):
    回收時機:從不回收; 使用:對象的一般保存 生命週期:JVM停止的時候纔會終止
  • 軟引用(SoftReference):
    回收時機:當內存不足的時候;使用:結合ReferenceQueue構造有效期短;生命週期:內存不足時終止
  • 弱引用(WeakReference):
    回收時機:在垃圾回收的時候;使用:同軟引用; 生命週期:GC後終止
  • 虛引用(PhatomReference):
    回收時機:在垃圾回收的時候;使用:結合ReferenceQueue來跟蹤對象垃圾回收期回收的活動; 生命週期:GC後終止

ListView是怎麼做性能優化的?

  1. convertView緩存重用
    分析:
    listview裏面有大量數據的item時,爲每一個Item創建一個View對象,必將佔用很多內存空間,所以必須要做內存處理;
    當一個item滑出去的時候它應該被回收,但是當用戶快速滑動的時候,如果回收就會造成頻繁的I/O操作(回收了又要重新從xml中生成view 這就是I/O操作)非常影響性能;
    針對這樣的問題,Android提供了一個叫做Recycler(反覆循環)的構件,滑出去的Item會被緩存到Recycler中,當滑完一屏再往下滑的時候,getView中的convertView參數就會複用滑出屏幕緩存的Item的View。從而大大改善性能。
  2. 圖片的緩存
    分析:
    上面提到的convertView緩存重用僅僅是對View的緩存重用,因而我們還要對View裏面的數據做緩存,這裏重點提到的是圖片;
    我們知道圖片是非常耗內存的,很容易造成內存溢出;我們可以用軟引用,但是已經不推薦了,我們常使用的第三方圖片加載框架 裏面用到的算法就是比較著名的Lrucache(LRU)算法—最近最少使用先回收 它是有LinkedHashMap實現的 感興趣的同學可以研究下它的源碼
  3. ViewHolder重用
    分析:
    我們都知道在getView()方法中的操作是這樣的:先從xml中創建view對象(inflate操作,我們已經採用了重用convertView方法優化),然後在這個view去findViewById,找到每一個item的子View,這也是一個耗時的操作。
    當第一次創建convertView對象時,便把這些item的子View保存到ViewHolder對象中。然後用convertView的setTag將viewHolder對象設置到Tag中, 當以後加載ListView的item時便可以直接從Tag中取出複用ViewHolder對象中的子View,不需要再findViewById找item的子View了。從而大大的提高了性能。
    值得一提的是:這裏我們可以用SparseArray(稀疏數組) 保存ViewHodler裏面的元素,可以寫成通用的方法,方便快捷的獲取ViewHodler裏面的元素,但其實不如直接寫ViewHodler的性能好。優點是加快開發速度和減少代碼維護。

    public class ViewHolder { 
        public static <T extends View> T get(View view, int id) {
        SparseArray<View> viewHolder = (SparseArray<View>) view.getTag(); 
        if (viewHolder == null) { 
            viewHolder = new SparseArray<View>(); 
            view.setTag(viewHolder); 
        } 
        View childView = viewHolder.get(id); 
        if (childView == null) { 
            childView = view.findViewById(id); 
            viewHolder.put(id, childView); 
        } 
        return (T) childView; 
        }
    }

    補充:SparseArray是android內部特有的api,標準的jdk是沒有這個類的,是android裏 <.Interger,Object> 這樣的Hashmap而專門寫的類,目的是提高效率,其核心是折半查找函數(binarySearch)

  4. 數據分頁加載等..
  5. 補充->ListView重複調用getview多次問題以及佈局優化(推薦)
    http://blog.csdn.net/f8376904110/article/details/6460934
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章