掌閱羣分享技術點收集(app性能優化專攻)

保活 先從老式最基礎的開始:html

  • 使用startService方式啓動一個獨立進程的服務,這樣系統會在service意外死亡後自動重啓。
  • 使用RTC定時鬧鐘每5分鐘檢測一下(4.0以上基本無效)
  • 啓動linux守護進程,每幾分鐘檢測一下進程是否存在,不存在就startService(5.0如下除MIUI和華爲外有效)
  • 5.0以上使用JobScheduler代替鬧鐘定時檢測啓動 。
  • 啓動隱藏的前臺通知。(支付寶即採用該方式,爲系統的一個bug,在7.1.1中已修復,具體體現爲下拉任務欄能夠看到該通知。)但這些措施都不能100%保活。尤爲是M中引入的doze模式,在doze模式中甚至無網絡鏈接。
  • 官方建議:1.引導用戶加入白名單;2.使用Google系列服務如GCM,Firebase

內存三級緩存思路 對於列表頁中的圖片應使用LRU之類的內存緩存,對於activity和fragment銷燬時應該把其對應的圖片釋放掉。每一個圖片都是有依賴引用的,咱們通常默認是fragment或者是activity,當activity或者fragment銷燬時,咱們會將只依賴當前頁面的圖片移出LRU進行釋放。java

  • 內存圖片緩存仍是有三級:一級是bitmap列表,一級是LRU強引用,一級是弱引用。加載圖片時,先在弱引用緩存和LRU強引用中查找沒有被釋放的bitmap,若是存在並知足複用條件,則不建立bitmap,而使用該圖片內存,以後將其從弱引用或者LRU中移出放入bitmap列表強引用中。bitmap列表強引用保存了該圖片依賴的直接控件。當view的onDetachedFromWindow被調用則從bitmap列表中移除只有依賴該view的對象到LRU強應用中。若是LRU強引用滿了則放到弱引用中。
  • 這種方案能夠達到以下效果:列表滑動、或者打開新頁面時會申請較小的內存或不申請內存。列表頁來回滑動時依然有緩存可用。
  • 對於adapterView和recycleView中的控件則需單獨寫個工具方法來封裝一下。
  • 這裏實際上已經不須要弱引用了,只要從LRU中移除便可調用recycle方法釋放該圖片。
  • 圖片內存的申請和釋放都由框架來控制,不禁gc管理。
  • LRU的大小就是可調,它裏面的圖片內存是均可以釋放的,它只是做爲緩存而存在。

卡頓問題快速定位的方法GPU monitor分析linux

  • 打開開發者模式中GPU呈現模式分析,查看是那種顏色條高
  • 若是是藍色偏高,說明是單位消息裏CPU太耗時,得把方法的執行都打出來看看哪一個耗時。好比,在某處先看看是否是應該出現onMeasure,而後能夠經過sdk自帶的View佈局工具,看一下哪一個View的onMeasure耗時最多。
  • 若是紅色偏高,說明GPU忙不過來。優化過渡繪製,使用離屏緩存來優化。
  • 黃色偏高,說明半透明GPU不只在忙着繪製你的window也還忙着繪製別的,可能的狀況爲透明window疊加多了,window裏的contentView有多個且相對複雜,或者GPU降頻了等等,想具體分析須要查看GPU的trace。
  • 畫動畫時藍色偏高是不正常的

**藍色偏高的常見緣由:**1. 動畫或者交互時緩存失效的太多,驗證方法是打出方法trace看看是否是有不少次的invalidate調用和dispatchDraw耗時在前面。2. 觸發了GC,驗證看看trace中主線程是否是被莫名的暫停了。3. 觸發了layout,這種藍色會很高,trace中measure方法會耗時較高。android

如何優化啓動速度 沒有閃屏頁activity,一旦存在閃屏頁activity那麼啓動速度就不大可能在200ms內跳過。 把window的背景設置爲閃屏頁,一旦MainActivity加載完畢就顯示主頁了。 雖然用戶也會看到一個相似的閃屏頁,但那個閃屏頁實際只是activity在theme中設置的background。 以前好像有人問我怎麼優化啓動速度。這個方案適合啓動畫面不是做爲廣告頁只是過渡頁使用的場景。git

<item name="android:windowBackground">@drawable/splash_logo</item>
複製代碼

自定義高性能可拖動GridView 在拖動過程當中沒有觸發過invalidate,也沒有觸發requestLayout,別的應用每次移動動畫會觸發notifyDateChange,這會觸發layout,影響性能。咱們在拖動過程當中會創建虛擬的視覺關係,只要不鬆手就會改變子view的順序,只有鬆手才觸發datechange。 提高性能繪製方法github

  • 調用方法把它移出可見區域,移出可見區域後,在進行繪製的時候 native 層也不會去繪製它。

TextView優化web

  • 列表直接不用textview,自定義view,經過提早緩存 StaticLayout提高TextView性能,可參考:http://ragnraok.github.io/textview-pre-render-research.html

Fragment留意點 每一個頁面都是Fragment,本身管理其生命週期和棧,每次啓動是以window的方式添加進來,進入動畫爲window動畫,手勢回退爲View動畫,爲了節省內存,頁面棧只保留2個對象,FragmentManager會進行回收釋放和Fragment的恢復。shell

  • 優勢:加載動畫很是流暢,內存佔用低,支持頁面的無窮層級疊加。
  • 缺點:以Window方式啓動,不少系統的特性須要本身實現,難以駕馭。

狀態欄兼容注意點 針對狀態欄咱們單獨適配了4.4以上版本,5.0以上版本,6.0以上版本,Flyme系統,6.0以上的Flyme系統,MIUI系統,6.0以上的MIUI系統,YunOS系統,VIVO Funtouch 2.5如下版本和2.5以上版本.......canvas

try-catchapi

  • 若是你的代碼必定會拋出異常,那try catch會有一些影響。
  • 一旦捕獲到異常,系統打到寄存器中獲取當前函數調用棧,生成一堆信息,這總歸是浪費性能的。
  • 因此說通常不影響,非要扣那確定影響,原本get方法能夠進行內聯的,用來try catch確定就不能進行內聯優化了,就會讓性能降低一點。
  • android這幾個版本推出了JIT,art虛擬機中重點對內聯函數的範圍進行擴充,try catch會阻止這些優化。
  • 在android 5.0以上(ART進行OPT優化時)全部函數中存在try catch的方法都不能被JIT優化和進行內聯優化

查看應用真實內存

  • adb shell dumpsys meminfo 你的包名 (monitor中不會顯示WebView的內存佔用)

  • (注意點)開了線程來執行耗時操做,但是這耗時操做執行的時候把主線的CPU佔用給搶了。。。。

高繪製性能函數 看一個ListView的函數

offsetChildrenTopAndBottom(int index)
複製代碼

這個方法性能很高,可是隱藏方法,listView移動子控件是用這個方法來移動的,它不破壞緩存,系統至關因而作了1+1+1+2,咱們本身作就是1+2+1+2+1+2,咱們本身寫ListView的時候發現了這個函數,咱們作for循環的性能仍是不如系統的這個隱藏方法。

使用windowManager的addView來添加控件 例子:經過調用Fragment的onCreateView來生成一個View,而後addView進來,這致使跳轉界面須要較長時間。如今得先addView一個View到windowManager中,而後在調用onCreateView,由於windowManager添加控件是在server進程,因此會當即addView進來,這時這裏的View就須要顯示點東西,這就須要相似windowBackground的東西來顯示。windowBackground它存在的目的就是爲了加快界面響應。

實現本身的windowbackground 先add一個相似decoreView進來,設置decoreView的background爲windowBackground,而後在往這個decoreView上添加實際的控件。

避免使用LayoutParams實現動畫

  • setLayoutParams()會觸發requestLayout()從而致使全部View從新measure、layout、draw,致使卡頓。 排查方法:能夠用佈局邊界排查大小變化的。 例:愛範兒 下拉刷新。(排查方法爲:重寫頂端控件的requestLayout方法,打上斷點,看看動畫或者交互過程當中誰調用到了這個方法)

  • 還有查看誰刷新了頁面致使重繪的排查方法是:重寫頂端控件的invalidateChildInParent方法,看看誰調用到了。

使用硬件離屏緩存進行優化。(要保證緩存不失效)

正確的使用:顯示硬件層更新綠色閃一下。

錯誤的使用:過程當中一直綠色。

錯誤例:微信大圖。

正確例:掌閱首頁切換
複製代碼
  • 硬件加速本質上是屬於window級別的東西,在建立ViewRootImpl的時候就肯定了是否使用硬件加速,View級別所謂的關閉只是建立一張bitmap而後調用View的draw方法往這個上面繪製,繪製完成再往硬件加速的canvas上繪製。
  • 系統對OpenGl方法進行了封裝和優化,封裝實現了canvas的方法,使用它有的時候比直接使用OpenGl性能還好。因此開硬件加速幾乎等效於調用OpenGl接口來繪製,OpenGl是通用繪製接口,通常GPU都會實現這些接口,因此硬件加速是讓GPU來繪製,而非硬件加速就是CPU本身繪製。
  • CPU要實現那麼多的通用計算,而GPU就那麼幾個簡單接口,它就極端優化,因此這幾個簡單方法的性能很是高。(OpenGl標準方法建立紋理很耗時,一張1080p的全屏圖須要40ms以上,而android系統本身私有的方法10ms之內就建立完畢了)
  • Opengl建立紋理(texture)太耗時,後面使用比系統的速度快,系統被它那套遞歸繪製等拖累了性能。(opengl來實現ViewPager的效果,android2.2手機除了初始化建立交互的紋理,進行移動的時候8ms左右一幀。)

主動釋放控件資源

//釋放佈局資源
   private void releaseDradable(View view){
        if (view instanceof ViewGroup) {
        int count = ((ViewGroup)view).getChildCount();
        for (int i=0;i<count;i++) {
             View childView = ((ViewGroup)view).getChildAt(i);
             if (childView instanceof ViewGroup) {
                  releaseDradable(childView);
               }else {
                releaseOneDrawable(childView);
              }
          }
         }else {
            releaseOneDrawable(view);
        }
  }
//釋放常見控件的資源,imageView是多種控件的根類
private void releaseOneDrawable(View view){
    if (view !=null) {
        BitmapDrawable src;
        BitmapDrawable backGround;
        if (view instanceof ImageView) {
            src = (BitmapDrawable) ((ImageView) view).getDrawable();
            recycleBitmap(src.getBitmap());
        }
        backGround = (BitmapDrawable) view.getBackground();
        recycleBitmap(backGround.getBitmap());
    }
}
private void recycleBitmap(Bitmap bitmap) {
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
        bitmap = null;    }
}
複製代碼

以上思路存在一個問題,即當某一個資源被多個activity引用時,回收該資源則會形成其餘持有該資源的activity發生異常。

  • 維護一個Drawable鏈表用以記錄引用次數

  • 將控件的getDrawable()和getBackground()設置爲null

系統Viewpager的性能優化

  • V4包裏的SwipeRefreshLayout類在接收到down事件的時候,會調用bringToFront方法,該方法會觸發requestLayout。

SwipeRefreshLayout.jpg

*這裏主要是優化ViewPager在添加和刪除item的時候,會觸發requestLayout致使的卡頓問題。第一次加載是沒有優化的,由於必須得觸發layout。 * 通過分析,咱們的場景不須要這個方法,就去掉了該方法, viewPager的adapter中instantiateItem()會執行container.addView(object),這也會觸發requestLayout;destroyItem也會觸發requestLayout。能夠替換爲attachViewToParent和detachViewFromParent方法來進行add和remove。這倆方法是listView中進行動態add和remove的方法,性能很高,不會讓緩存失效和觸發requestLayout。

咱們的ViewPager的setOffscreenPageLimit設置爲1。調用detachViewFromParent方法後爲了讓ViewPager從新錄製一下View的繪製,因此又手動調用了invalidate。 錄製繪製就是dispatchDraw流程,否則會走getDisplayList流程

destroyItem.jpg

在 instantateItem()中調用以下代碼

調用代碼.png

instantateItem.jpg

微信db打開方式 用戶設備的IMEI+uin值計算MD5值,注意是小寫字符,而後在取MD5的前7位字符構成的密碼。

關於RelativeLayout的使用 大量使用了RelativeLayout,致使了屢次mesure,一個relativelayout都要measure兩次,多個層次這種疊加以後,measure次數指數級上升。

關於 SoftwareRefrence 在android低版本上,SoftwareRefrence是遵循java標準的GC回收流程,即只有觸發GC的狀況爲內存不足時,纔會去檢查SoftReference,但在高版本上,SoftReference被檢查的更頻繁了,即不是隻有內存不足時纔去檢查,其存在的機率與WeakReference接近。

FragmentTabHost的問題 每次FragmentTabHost切換fragment時會調用onCreateView()重繪UI。 解決方法:

private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
		TabInfo newTab = null;
		for (int i = 0; i < mTabs.size(); i++) {
			TabInfo tab = mTabs.get(i);
			if (tab.tag.equals(tabId)) {
				newTab = tab;
			}
		}
		if (newTab == null) {
			throw new IllegalStateException("No tab known for tag " + tabId);
		}
		if (mLastTab != newTab) {
			if (ft == null) {
				ft = mFragmentManager.beginTransaction();
			}
			if (mLastTab != null) {
				if (mLastTab.fragment != null) {
					// 將detach替換爲hide,隱藏Fragment
					// ft.detach(mLastTab.fragment);
					ft.hide(mLastTab.fragment);
				}
			}
			if (newTab != null) {
				if (newTab.fragment == null) {
					newTab.fragment = Fragment.instantiate(mContext,
							newTab.clss.getName(), newTab.args);
					ft.add(mContainerId, newTab.fragment, newTab.tag);
				} else {
					// 將attach替換爲show,顯示Fragment
					// ft.attach(newTab.fragment);
					ft.show(newTab.fragment);
				}
			}

			mLastTab = newTab;
		}
		return ft;
	}
複製代碼

app啓動優化

  • 以前優化軟件啓動時間的時候,就是Application的attachBaseContext()開啓method trace,首頁的dispatchDraw方法被調用以後關閉,而後這段時間CPU都幹了什麼。

  • 耗時的操做放到了dispatchDraw方法以後post一個回調來執行。

    Looper.getMainLooper().setMessageLogging(new Printer() {
             @Override
             public void println(String x) {
                 Log.e("msg", x);
             }
         });
    複製代碼

用這個來測算每一個消息花了多長時間,若是消息的執行時間超過了16ms,則獲取當前的函數調用棧。 (能夠確保你的耗時操做在頁面顯示以後才執行。)

  • 或者還有個onPostResume()方法,咱們如今已經不用View的繪製以後再執行操做了,咱們改成放到onPostResume方法中執行。(draw方法執行了,頁面就顯示了,而後不會黑屏或者白屏了)
  • app啓動流程:Application的構造方法->attachBaseContext()->onCreate()->Activity的構造方法->onCreate()->配置主題中背景燈屬性->onStart()...

關於inflate

  • inflate自己是io操做,而手機性能降低很大的一個緣由就是io性能變差。

  • 動態添加能夠解決 xml 加載時間問題;自定義view 能夠解決嵌套層級問題。

strings文件下多個同種類型字符串的問題

  • 共有%1s條報價,已下%2s單
  • ]]>

RecyclerView的item的其餘思路

  • recyleView中全部類型的item均爲繼承View,內容徹底本身canvas繪製(一個個add進去),View中保存每一個item的狀態,得到該狀態則可繪製出該View,bindView中無xml的inflate,已展現過的item再次顯示時無需measure和耗時計算,ViewPager中limited item數爲默認1,item被移除時,View內存被釋放,再次進入時依靠保存的數據復原原item,此爲同步操做。目前看來複原速度很快,用戶對其是復原仍是緩存的是感知不出來的。
  • android的新版本上也measure的結果進行了緩存,文本的測量也使用了100多K的空間進行全局緩存。

今日頭條跟手回退實現--羣分享記錄

  • 今日頭條也是基於Activity的透明主題來實現的,可是這個方案都有兩個缺點,一就是疊加層級一多,滑動性能會降低明顯,基本疊加5層就很卡了,二是透明主題破壞了系統內存回收釋放的策略,致使全部的activity都是前臺Activity,系統都不會回收,就會OOM。解決這個問題有一個方案就是利用android 4.4裏提供的動態設置Activity透明主題來實現,當疊加了三層以後就將底部的第三層改成非透明主題。

    /**
            * 動態將一個activity設置爲不透明主題
            *
           * @param activity
           */
          public static void convertActivityFromTranslucent(Activity activity) {
                   try {
                          Method method = Activity.class.getDeclaredMethod("convertFromTranslucent");
                          method.setAccessible(true);
                          method.invoke(activity);
                       } catch (Throwable t) {
                      }
        }
    複製代碼
/** * 動態將一個activity設置爲透明主題 * * @param activity */
   public static void convertActivityToTranslucent(Activity activity) {
       try {
           Class<?>[] classes = Activity.class.getDeclaredClasses();
           Class<?> translucentConversionListenerClazz = null;
           for (Class clazz : classes) {
               if (clazz.getSimpleName().contains(
                       "TranslucentConversionListener")) {
                   translucentConversionListenerClazz = clazz;
               }
           }
           if (Build.VERSION.SDK_INT < 21) {//這個也僅支持4.4及以上
               Method method = Activity.class.getDeclaredMethod(
                       "convertToTranslucent",
                       translucentConversionListenerClazz);
               method.setAccessible(true);
               method.invoke(activity, new Object[]{null});
           } else {//5.0以上的系統
               Method method = Activity.class.getDeclaredMethod(
                       "convertToTranslucent",
                       translucentConversionListenerClazz,
                       ActivityOptions.class);
               method.setAccessible(true);
               method.invoke(activity, null, null);
           }
       } catch (Throwable t) {
           t.printStackTrace();
       }
   }

複製代碼

反編譯某app.jpg

  • 反編譯了QQ空間的apk,他們把每一個類的構造函數中添加了一行Zygote.class.getName(),Zygote是系統中存在的一個類。他們選用Zygote是android SDK中不存在的,被隱藏的類,他們應該是認爲之後每一個版本系統中都會有這個類,因此才選擇了它。那麼系統進行檢查的時候就不會認爲初始化該類只須要使用當前Dex。(防止CLASS_ISPREVERIFIED

  • animation有個onAnimationStart和onAnmationEnd方法裏面不可以使用addView/removeView的方法,有可用handler.postRunable()來執行;開啓硬件加速的時候有一些手機上會有機率性問題。硬件加速中draw流程只是進行錄製,若是在錄製的以後進行繪製的時候發現以前錄製的已經無效了,在4.X的機型上就可能發生崩潰。動畫的回調是在draw流程中執行的,在回調進行動態的removeView就會致使錄製的繪製命令無效。

  • 線程的使用

ArrayList <File> list = new ArrayList<>();
    public void scanDir(String dirString){
        File dirFile = new File(dirString);
        list.add(dirFile);
        while (list.size() > 0){
            File files[] = list.remove(0).listFiles();
            for(File file : files){
                if(file.isDirectory()){
                    list.add(file);
                }else{
                    if(file.getAbsolutePath().endsWith("mp3")){
                        Log.e("", "這是音樂文件");
                    }
                }
            }
        }
    }
複製代碼

把遞歸變成隊列,再把隊列變成多線程執行,下面只須要開啓多個線程來執行scanDir(),好比一共有n+1(CPU核心數+1)個線程來執行scanDir(),當list有第一個元素時開啓一個線程執行scanDir(),這個線程會往list中繼續添加元素,當開啓的線程小於n+1時,繼續開啓線程,直到達到n+1,達到以後就等待線程執行完畢,其中某個線程執行完畢以後再次去list中獲取底部的元素來執行scanDir,直到list大小爲0。

  • LayoutParams 的問題 直接new出來 View 若是不設置LayoutParams 就 add進一個viewgroup類型 它的LayoutParams 是由 父viewgroup generateDefaultLayoutParams函數 決定的。

view1.png

  • 類型強轉的注意點 強轉前加 if xxx instanceof xx的校驗,典型問題: 兼容包下的控件getContext不能強轉爲Activity(佈局文件寫入控件,activity繼承AppCompatActivity ) 追溯View第二個構造函數發現context是LayoutInfalte傳來的,發現是LayoutInflater.from傳來的,發是phoneWindow傳來的 發現newPhoneWindow(Activity) ,這樣按理說view的context自己就是activity,但是報錯說是 tintContextWrapper

ImageView.png

  • activity在作動畫的時候,頁面的繪製是暫停的,或者只是繪製幾幀。調用一個方法可讓其在動畫過程當中不暫停繪製。 反射調用ViewRootImpl 中的 setDrawDuringWindowsAnimating(true) 在onAttachedToWindow後`調用(api 19及以上)相似的能夠有:
private boolean sInited = false;
   private Method msetDrawDuringWindowsAnimatingMethod;
   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && Build.VERSION.SDK_INT< 24) {
           Object viewRootImpl = ((Activity)getContext()).getWindow().getDecorView().getParent();
           if (!sInited) {
               try {
                   msetDrawDuringWindowsAnimatingMethod = viewRootImpl.getClass().getMethod("setDrawDuringWindowsAnimating", boolean.class);
               } catch (NoSuchMethodException e) {
                   e.printStackTrace();
               }
               if (msetDrawDuringWindowsAnimatingMethod != null) {
                   try {
                       msetDrawDuringWindowsAnimatingMethod.invoke(viewRootImpl, true);
                   } catch (IllegalAccessException e) {
                       e.printStackTrace();
                   } catch (InvocationTargetException e) {
                       e.printStackTrace();
                   }
               }
               sInited = true;
           }
       }
   }
複製代碼
  • 優化View的inflate 若是你發現建立某個View的inflate很耗時,或者是measure和layout很耗時,你就只有採用相似這樣的方式來優化它

掌閱app代碼.jpg

耗時的工做完成的,把addViewInLayout之類的post到主線程就好了。 只有被添加到View樹上的時候纔可能會繪製,剛inflate出來的View沒有被添加到View樹上,因此不會進行measure和layout。measure是耗時的,若是在線程中執行,它就會減小主線程的卡頓,最後再添加到View樹上,而且不用觸發requestLayout。至關於異步加載view 使用場景:好比進入一個頁面開啓網絡加載一段內容,當網絡數據回來了就刷新頁面,若是這個頁面是列表,那麼若是是正在滑動的時候刷新頁面就會卡頓,這時就可使用這種方式來進行優化。

  • 利用aapt解析apk信息

aapt dump badging demo.apk |grep version

aapt的其餘參數,也比較使用,好比向apl中插入文件,刪除文件,這個和unzip的效果是同樣同樣的。過去沒用appt時,咱們修改apk信息常常用zip/unzip,如今用appt也能夠搞定,還能避免有的系統沒有安裝zip/unzip的問題

aapt a demo.apk test.txt

  • Activity被銷燬分爲兩種:1. Activity對象被從ActivityThread中移除了,這時只是把java對象置null,若是你其餘地方還持有該對象,這個activity是不會被釋放的。 2. Activity所在的進程被回收,那它全部的資源都被回收了。只會有一些可序列化的數據被保存。

  • 如下條件webview能夠關閉硬件加速

關閉硬件加速.jpg

//mModelNumber = Build.MODEL;
複製代碼
  • 獲取GPU刷新幀率

GPU幀率.png

能夠利用Looper的log機制和添加全局控件來本身實現那個柱狀圖。

  • 列表優化思路 1.item中不能使用任何xml,包括xml的drawable;2.減小View個數層級下降過分繪製;3.本身實現TextView,系統的TextView(特別是android 7.0如下系統)性能太爛。

  • anr adb shell ls /data/anr/

    adb pull /data/anr/traces.txt ~/Desktop

屏幕快照 2017-11-08 下午3.00.04.png

  • Handler中一段泄漏風險檢測的代碼
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

該思想可用於多種自測內存泄漏的場景。

  • 使用遞歸時,留意函數棧是否會爆
  • 線程池的大小經驗值設置:(其中N爲CPU的個數)

若是是CPU密集型應用,則線程池大小設置爲N+1, 若是是IO密集型應用,則線程池大小設置爲2N+1

若是一臺服務器上只部署這一個應用而且只有這一個線程池,那麼這種估算或許合理,具體還需自行測試驗證。可是,IO優化中,這樣的估算公式可能更適合:最佳線程數目 =((線程等待時間+線程CPU時間)/線程CPU時間 )* CPU數目由於很顯然,線程等待時間所佔比例越高,須要越多線程。線程CPU時間所佔比例越高,須要越少線程。下面舉個例子:好比平均每一個線程CPU運行時間爲0.5s,而線程等待時間(非CPU運行時間,好比IO)爲1.5s,CPU核心數爲8,那麼根據上面這個公式估算獲得: ((0.5+1.5)/0.5)*8=32。這個公式進一步轉化爲:最佳線程數目 = (線程等待時間與線程CPU時間之比 + 1)* CPU數目

相關文章
相關標籤/搜索