


版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Android系統(tǒng)穩(wěn)定性-ANR1.2如何分析ANR問題引起ANR問題的根本原因,總的來說可以歸納為兩類:應(yīng)用進(jìn)程自身引起的,例如:主線程阻塞、掛起、死循環(huán)應(yīng)用進(jìn)程的其他線程的CPU占用率高,使得主線程無法搶占到CPU寸間片其他進(jìn)程間接引起的,例如:當(dāng)前應(yīng)用進(jìn)程進(jìn)行進(jìn)程間通信請(qǐng)求其他進(jìn)程,其他進(jìn)程的操作長時(shí)間沒有反饋其他進(jìn)程的CPU占用率高,使得當(dāng)前應(yīng)用進(jìn)程無法搶占到CPU寸間片分析ANR問題時(shí),以上述可能的幾種原因?yàn)榫€索,通過分析各種日志信息,大多數(shù)情況下你就可以很容易找到問題所在了。注意:確實(shí)有一些ANR問題很難調(diào)查清楚,因?yàn)檎麄€(gè)系統(tǒng)不穩(wěn)定的因素很多,例如LinuxKernel本身的bug
2、引起的內(nèi)存碎片過多、硬件損壞等。這類比較底層的原因引起的ANR、可題往往無從查起,并且這根本不是應(yīng)用程序的問題,浪費(fèi)了應(yīng)用開發(fā)人員很多時(shí)間,如果你從事過整個(gè)系統(tǒng)的開發(fā)和維護(hù)工作的話會(huì)深有體會(huì)。所以我不能保證了解了本章的所有內(nèi)容后能夠解決一切ANR、可題,如果出現(xiàn)了很疑難的ANR、可題,我建議最好去和做驅(qū)動(dòng)和內(nèi)核的朋友聊聊,或者,如果問題只是個(gè)十萬分之一的偶然現(xiàn)象,不影響程序的正常運(yùn)行,我倒是建議不去理它。1.2.1分析ANR勺利器Android會(huì)在ANR生時(shí)輸出很多有用的信息幫助分析問題原因,我們先來看一下ANR勺異常信息,使用logcat命令查看會(huì)得到類似如下的log:/WindowMana
3、ger所在的進(jìn)程是system_server,進(jìn)程號(hào)是127I/WindowManager(127):Inputeventdispatchingtimedoutsendingtocom.example.anrdemo/com.example.anrdemo.ANRActivity/system_server進(jìn)程中的ActivityManagerService請(qǐng)求kernel向5033進(jìn)程發(fā)送SIGNAL_QUIT肯求你可以在shell中使用命令達(dá)到相同的目的:adbshellkill-35033/和其他的Java虛擬機(jī)一樣,SIGNAL_QUI如是Dalvik內(nèi)部支持的功能之I/Process
4、(127):Sendingsignal.PID:5033SIG:3/5033進(jìn)程的虛擬機(jī)實(shí)例接收到SIGNAL_QUIT信號(hào)后會(huì)將進(jìn)程中各個(gè)線程的函數(shù)堆棧信息輸出到traces.txt文件中/發(fā)生ANR的進(jìn)程正常情況下會(huì)第一個(gè)輸出I/dalvikvm(5033):threadid=4:reactingtosignal3I/dalvikvm(5033):Wrotestacktracesto'/data/anr/traces.txt'/另外還有其他一些進(jìn)程/隨后會(huì)輸出CPUS用情況E/ActivityManager(127):ANRincom.example.anrdemo(com
5、.example.anrdemo/.ANRActivity)/Reason表示導(dǎo)致ANR'可題的直接原因E/ActivityManager(127):Reason:keyDispatchingTimedOutE/ActivityManager(127):Load:3.85/3.41/3.16/請(qǐng)注意ago,表示ANR生之前的一段時(shí)間內(nèi)的CPU使用率,并不是某一時(shí)刻的值E/ActivityManager(127):CPUusagefrom26835msto3662msagowith99%awake:E/ActivityManager(127):9.4%98/mediaserver:9.4
6、%user+0%kernelE/ActivityManager(127):8.9%127/system_server:6.9%user+2%kernel/faults:1823minor.E/ActivityManager(127):+0%5033/com.example.anrdemo:0%user+0%kernelE/ActivityManager(127):39%TOTAL:32%user+6.1%kernel/這里是later,表示ANR®生之后E/ActivityManager(127):CPUusagefrom601msto1132mslaterwith99%awake:
7、E/ActivityManager(127):10%127/system_server:1.7%user+8.9%kernel/faults:5minorE/ActivityManager(127):10%163/InputDispatcher:1.7%user+8.9%kernelE/ActivityManager(127):1.7%127/system_server:1.7%user+0%kernelE/ActivityManager(127):1.7%135/SurfaceFlinger:0%user+1.7%kernelE/ActivityManager(127):1.7%2814/B
8、inderThread#:1.7%user+0%kernelE/ActivityManager(127):37%TOTAL:27%user+9.2%kernel從log中能夠知道,發(fā)生ANR寸Android為我們提供了兩種“利器”:traces文件和CPU®用率。以上做了簡(jiǎn)單注釋,不過稍后再詳細(xì)分析它們。1.2.2ANR信息是如何輸出的我們?cè)賮砜纯催@些log是怎樣被輸出的,這很重要,知其然,也要知其所以然。代碼在ActivityManagerService類中,找到它的appNotResponding函數(shù)。finalvoidappNotResponding(ProcessRecord
9、app,ActivityRecordactivity,ActivityRecordparent,finalStringannotation)/firstPids和lastPids兩個(gè)集合存放那些將會(huì)在traces中輸出信息的進(jìn)程的進(jìn)程號(hào)ArrayList<Integer>firstPids=newArrayList<Integer>(5);SparseArray<Boolean>lastPids=newSparseArray<Boolean>(20);/mController是IActivityController接口的實(shí)例,是為Monkey測(cè)試
10、程序預(yù)留的,默認(rèn)為nullif(mController!=null).longanrTime=SystemClock.uptimeMillis();if(MONITOR_CPU_USAGE)(updateCpuStatsNow();/更新CPUf£用率synchronized(this)(/一些特定條件下會(huì)忽略ANRif(mShuttingDown)(Slog.i(TAG,"DuringshutdownskippingANR:"+app+""+annotation);return;elseif(app.notResponding)(Slog.i
11、(TAG,"SkippingduplicateANR:"+app+""+annotation);return;elseif(app.crashing)(Slog.i(TAG,"CrashingappskippingANR:"+app+""+annotation);return;/使用一個(gè)標(biāo)志變量避免同一個(gè)應(yīng)用在沒有處理完時(shí)重復(fù)輸出logapp.notResponding=true;./當(dāng)前發(fā)生ANR的應(yīng)用進(jìn)程被第一個(gè)添加進(jìn)firstPids集合中firstPids.add(app.pid);/dumpStackTr
12、aces是輸出traces文件的函數(shù)FiletracesFile=dumpStackTraces(true,firstPids,processStats,lastPids,null);StringcpuInfo=null;if(MONITOR_CPU_USAGE)(/MONITOR_CPU_USAGE認(rèn)為trueupdateCpuStatsNow();/再次更新CPU言息synchronized(mProcessStatsThread)(輸出ANR生前一段時(shí)間內(nèi)的CPUS用率cpuInfo=mProcessStatsprintCurrentState(anrTime);info-appendl
13、processStatsgrintCurrentLoad。);infoappend(cpuInfo);輸出ANR生后一段時(shí)間內(nèi)的CPUS用率infoappend(processStatsprintCurrentState(anrTime);/將ANR信息同時(shí)輸出到DropBox中addErrorToDropBox("anr",app,cessName,activity,parent,annotation,cpuInfo,tracesFile,null);./在Android4.0中可以設(shè)置是否不顯示AN破示對(duì)話框,如果設(shè)置的話就不會(huì)顯示對(duì)話框,并且會(huì)殺掉ANR
14、®程booleanshowBackground=Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND,0)!=0;synchronized(this)if(!showBackground&&!app.isInterestingToUserLocked()&&app.pid!=MY_PID).Process.killProcessQuiet(app.pid);return;./顯示anrH示對(duì)話框Messagemsg=Message
15、.obtain();HashMapmap=newHashMap();msg.what=SHOW_NOT_RESPONDING_MSG;msg.obj=map;map.put("app",app);if(activity!=null)(map.put("activity",activity);mHandler.sendMessage(msg);有三個(gè)關(guān)鍵點(diǎn)需要注意: 當(dāng)前發(fā)生ANR勺應(yīng)用進(jìn)程被第一個(gè)添加進(jìn)firstPids集合中,所以會(huì)第一個(gè)向traces文件中寫入信息。反過來說,traces文件中出現(xiàn)的第一個(gè)進(jìn)程正常情況下就是發(fā)生ANR勺那個(gè)進(jìn)程。不過有
16、時(shí)候會(huì)很不湊巧,發(fā)生ANR勺進(jìn)程還沒有來得及輸出trace信息,就由于某種原因退出了,所以偶爾會(huì)遇到traces文件中找不到發(fā)生ANR勺進(jìn)程信息的情況。 dumpStackTraces是輸出traces文件的函數(shù),接下來分析這個(gè)函數(shù)addErrorToDropBox函數(shù)將ANRR言息同時(shí)輸出到DropBox中,它也是個(gè)非常有用的日志存放工具,后面也會(huì)分析它的作用。publicstaticFiledumpStackTraces(booleanclearTraces,ArrayList<Integer>firstPids,ProcessStatsprocessStats,SparseA
17、rray<Boolean>lastPids,StringnativeProcs)(/系統(tǒng)屆性“dalvik.vm.stack-trace-file”用來配置trace信息輸出文件StringtracesPath=SystemProperties.get("dalvik.vm.stack-trace-file",null);if(tracesPath=null|tracesPath.length()=0)(returnnull;FiletracesFile=newFile(tracesPath);try(FiletracesDir=tracesFile.getPa
18、rentFile();if(!tracesDir.exists()tracesFile.mkdirs();/FileUtils.setPermissions是個(gè)很有用的函數(shù),設(shè)置文件屆性時(shí)經(jīng)常會(huì)用到FileUtils.setPermissions(tracesDir.getPath(),0775,-1,-1);/drwxrwxr-x/clearTraces為true,會(huì)刪除舊文件,創(chuàng)建新文件if(clearTraces&&tracesFile.exists()tracesFile.delete();tracesFile.createNewFile();FileUtils.set
19、Permissions(tracesFile.getPath(),0666,-1,-1);/-rw-rw-rw-catch(IOExceptione)(Slog.w(TAG,"UnabletoprepareANRtracesfile:"+tracesPath,e);returnnull;/一個(gè)重載函數(shù)dumpStackTraces(tracesPath,firstPids,processStats,lastPids,nativeProcs);returntracesFile;有兩個(gè)關(guān)鍵點(diǎn)需要注意: 聰明的你肯定已經(jīng)知道,之所以trace信息會(huì)輸出到“/data/anr/tr
20、aces.txt”文件中,就是系統(tǒng)屬性“dalvik.vm.stack-trace-file”設(shè)置的。你可以通過在設(shè)備的shell中使用setprop和getprop對(duì)系統(tǒng)屬性進(jìn)行設(shè)置和讀?。?getpropdalvik.vm.stack-trace-filesetpropdalvik.vm.stack-trace-file/tmp/stack-traces.txt每次發(fā)生ANR寸都會(huì)刪除舊的traces文件,重新創(chuàng)建新文件。也就是說Android只保留最后一次發(fā)生ANR寸的traces信息,那么以前的traces信息就丟失了么?稍后回答。接著來看重載的dumpStackTraces函數(shù)。pr
21、ivatestaticvoiddumpStackTraces(StringtracesPath,ArrayList<Integer>firstPids,ProcessStatsprocessStats,SparseArray<Boolean>lastPids,StringnativeProcs)/使用FileObserver監(jiān)聽?wèi)?yīng)用進(jìn)程是否已經(jīng)完成寫入traces文件的操作/Android在判斷桌面壁紙文件是否設(shè)置完成時(shí)也是用的FileObserver,很有用的類FileObserverobserver=newFileObserver(tracesPath,FileOb
22、server.CLOSE_WRITE)publicsynchronizedvoidonEvent(intevent,Stringpath)notify();./首先輸出firstPids集合中指定的進(jìn)程,這些也是對(duì)ANR問題來說最重要的進(jìn)程if(firstPids!=null)tryintnum=firstPids.size();for(inti=0;i<num;i+)synchronized(observer)/前面提到的SIGNAL_QUITProcess.sendSignal(firstPids.get(i),Process.SIGNAL_QUIT);observer.wait(2
23、00);提示:如果你在解決其他問題時(shí)也需要查看Java進(jìn)程中各個(gè)線程的函數(shù)堆棧信息,就可以使用向目標(biāo)進(jìn)程發(fā)送SIGNAL_QUIT(3)這個(gè)技巧。其實(shí)這個(gè)名稱有點(diǎn)誤導(dǎo),它并不會(huì)讓進(jìn)程真正退出。1.2.3DropBox剛才留了一個(gè)問題:Android只保留最后一次發(fā)生ANF®寸的traces信息,那么以前的traces信息就丟失了么?為了增強(qiáng)Android的異常信息收集管理能力,從2.2開始增加了DropBox功能。DropBox(簡(jiǎn)稱D0是系統(tǒng)進(jìn)程中的一個(gè)服務(wù),在system_server進(jìn)程啟動(dòng)時(shí)創(chuàng)建,并且它沒有運(yùn)行在單獨(dú)的線程中,而是運(yùn)行在system_server的Server
24、Thread線程中。我們可以將ServerThread稱作system_server的主線程,ServerThread線程除了啟動(dòng)并維護(hù)各個(gè)服務(wù)外,還負(fù)責(zé)檢測(cè)一些重要的服務(wù)是否死鎖,這一點(diǎn)到后面的Watchdog部分再分析<!-Watchdog寫完后注意補(bǔ)充章節(jié)號(hào)->。DB被創(chuàng)建的代碼如下。SystemServer.javaServerThread.run()Slog.i(TAG"DropBoxService");ServiceManager.addService(Context.DROPBOX_SERVICE服務(wù)名稱為“dropbox”newDropBoxMa
25、nagerService(context,newFile("/data/system/dropbox");“/data/system/dropbox”是DB指定的文件存放位置。下面來看一下DB服務(wù)的主要功能。1.DropBoxManagerServiceDropBoxManagerService(簡(jiǎn)稱DBMS就是DB服務(wù)的本尊,它的主要功能接口包括以下幾個(gè)函數(shù):publicvoidadd(DropBoxManager.Entryentry)DBM翳所有要添加的日志都用DropBoxManager.Entry類型的對(duì)象表示,通過add函數(shù)添加,并且直到目前為止一個(gè)Entry對(duì)
26、象對(duì)應(yīng)著一個(gè)日志文件。publicbooleanisTagEnabled(Stringtag)通過給每一個(gè)Entry設(shè)置一個(gè)tag可以標(biāo)識(shí)不同類型的日志,并且可以靈活的啟用/禁用某種類型的日志,isTagEnabled用來判斷指定類型的日志是否被啟用/禁用了,一旦禁用就不會(huì)再記錄這種類型的日志。默認(rèn)是不禁用任何類型的日志的。稍后說明如何啟用/禁用日志。publicsynchronizedDropBoxManager.EntrygetNextEntry(Stringtag,longmillis)我們可以通過getNextEntry函數(shù)獲取指定類型和指定時(shí)間點(diǎn)之后的第一條日志,要使用這個(gè)功能應(yīng)用程
27、序需要有“android.permission.READ_LOGS”的權(quán)限,并且在使用完畢返回的Entry對(duì)象后要調(diào)用其close函數(shù)確保關(guān)閉日志文件的文件描述符(如果不關(guān)閉的話可能造成進(jìn)程打開的文件描述符超過1024而崩潰,Android中限制每個(gè)進(jìn)程的文件描述符上限為1024)。DBM洗供了很多的配置項(xiàng)用來限制對(duì)磁盤的使用,通過SettingsProvider應(yīng)用程序維護(hù),數(shù)據(jù)存放在其settings.db數(shù)據(jù)庫中。這些配置項(xiàng)也都有默認(rèn)值,羅列如下:Settings.Secure.DROPBOX_AGE_SECONDS="dropbox_age_seconds"日志文件
28、保存的最長時(shí)間,默認(rèn)3天Settings.Secure.DROPBOX_MAX_FILES="dropbox_max_files"日志文件的最大數(shù)最,默認(rèn)值是1000Settings.Secure.DROPBOX_QUOTA_KB="dropbox_quota_kb"磁盤空間最大使用景Settings.Secure.DROPBOX_QUOTA_PERCENT="dropbox_quota_percent"Settings.Secure.DROPBOX_RESERVE_PERCENT="dropbox_reserve_perc
29、ent"Settings.Secure.DROPBOX_TAG_PREFIX="dropbox:"應(yīng)用程序可以利用DropBox來做事情,收集日志等1.2.4traces.txt終于到大明星出場(chǎng)的時(shí)候了,一起來看一下traces.txt的廬山真面目c以下是筆者寫的一個(gè)演示程序制造出的一次ANR勺trace信息:/文件中輸出的第一個(gè)進(jìn)程的trace信息,正是發(fā)生ANR勺演示程序/開頭顯示進(jìn)程號(hào)、ANRt生的時(shí)間點(diǎn)和進(jìn)程名稱-pid9183at2012-09-2822:20:42-Cmdline:com.example.anrdemoDALVIKTHREADS:/以下
30、是各個(gè)線程的函數(shù)堆棧信息/mutexes表示虛擬機(jī)實(shí)例中各種線程相關(guān)對(duì)象鎖的value值(mutexes:tll=0tsl=0tscl=0ghl=0hwl=0hwll=0)/依次是:線程名、線程優(yōu)先級(jí)、線程創(chuàng)建時(shí)的序號(hào)、線程當(dāng)前狀態(tài)"main"prio=5tid=1TIMED_WAIT/依次是:線程組名稱、suspendCount、debugSuspendCount、線程的Java對(duì)象地址、線程的Native對(duì)象地址|group="main"sCount=1dsCount=0obj=0x4025b1b8self=0xce68/sysTid是線程號(hào),主線程
31、的線程號(hào)和進(jìn)程號(hào)相同|sysTid=9183nice=0sched=0/0cgrp=defaulthandle=-1345002368|schedstat=(140838632210998525213)atjava.lang.VMThread.sleep(NativeMethod)atjava.lang.Thread.sleep(Thread.java:1213)atjava.lang.Thread.sleep(Thread.java:1195)atcom.example.anrdemo.ANRActivity.makeANR(ANRActivity.java:44)atcom.example
32、.anrdemo.ANRActivity.onClick(ANRActivity.java:38)atandroid.view.View.performClick(View.java:2486)atandroid.view.View$PerformClick.run(View.java:9130)atandroid.os.Handler.handleCallback(Handler.java:587)atandroid.os.Handler.dispatchMessage(Handler.java:92)atandroid.os.Looper.loop(Looper.java:130)atan
33、droid.app.ActivityThread.main(ActivityThread.java:3703)atjava.lang.reflect.Method.invokeNative(NativeMethod)atjava.lang.reflect.Method.invoke(Method.java:507)ernal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)ernal.os.ZygoteInit.main(ZygoteInit.java:599
34、)atdalvik.system.NativeStart.main(NativeMethod)/Binder線程是進(jìn)程的線程池中用來處理binder請(qǐng)求的線程"BinderThread#2"prio=5tid=8NATIVE|group="main"sCount=1dsCount=0obj=0x40750b90self=0x1440b8|sysTid=9190nice=0sched=0/0cgrp=defaulthandle=1476256|schedstat=(915528184631354)atdalvik.system.NativeStart.ru
35、n(NativeMethod)"BinderThread#1"prio=5tid=7NATIVE|group="main"sCount=1dsCount=0obj=0x4074f848self=0x78d40|sysTid=9189nice=0sched=0/0cgrp=defaulthandle=1308088|schedstat=(35095232554321210)atdalvik.system.NativeStart.run(NativeMethod)/線程名稱后面標(biāo)識(shí)有daemon,說明這是個(gè)守護(hù)線程"Compiler"da
36、emonprio=5tid=6VMWAIT|group="system"sCount=1dsCount=0obj=0x4074b928self=0x141e78|sysTid=9188nice=0sched=0/0cgrp=defaulthandle=1506000|schedstat=(2160643821636964101)atdalvik.system.NativeStart.run(NativeMethod)/JDWP線程是支持虛擬機(jī)調(diào)試的線程,不需要關(guān)心"JDWP"daemonprio=5tid=5VMWAIT|group="syst
37、em"sCount=1dsCount=0obj=0x4074b878self=0x16c958|sysTid=9187nice=0sched=0/0cgrp=defaulthandle=1510224|schedstat=(36621128076177)atdalvik.system.NativeStart.run(NativeMethod)/"SignalCatcher”負(fù)責(zé)接收和處理kernel發(fā)送的各種信號(hào),例如SIGNAL_QUITSIGNAL_USR1就是被該線程/接收到,這個(gè)文件的內(nèi)容就是由該線程負(fù)責(zé)輸出的,可以看到它的狀態(tài)是RUNNABLE不過此線程也不需要關(guān)
38、心"SignalCatcher"daemonprio=5tid=4RUNNABLE|group="system"sCount=0dsCount=0obj=0x4074b7b8self=0x150008|sysTid=9186nice=0sched=0/0cgrp=defaulthandle=1501664|schedstat=(170898562866219)atdalvik.system.NativeStart.run(NativeMethod)"GC"daemonprio=5tid=3VMWAIT|group="syst
39、em"sCount=1dsCount=0obj=0x4074b710self=0x168010|sysTid=9185nice=0sched=0/0cgrp=defaulthandle=1503184|schedstat=(30517648217782)atdalvik.system.NativeStart.run(NativeMethod)"HeapWorker"daemonprio=5tid=2VMWAIT|group="system"sCount=1dsCount=0obj=0x4074b658self=0x16a080|sysTid=9
40、184nice=0sched=0/0cgrp=defaulthandle=550856|schedstat=(336914072633666915)atdalvik.system.NativeStart.run(NativeMethod)-end9183-pid127at2012-09-2822:20:42-Cmdline:system_server./省略其他進(jìn)程的信息有一個(gè)關(guān)鍵點(diǎn)需要注意:?線程有很多狀態(tài),了解這些狀態(tài)的意義對(duì)分析ANR勺原因是有幫助的,總結(jié)如下:Thread.java中定義的狀態(tài)Thread.cpp中定義的狀說明態(tài)TERMINATEDZOMBIE線程死亡,終止運(yùn)行RUNN
41、ABLERUNNING/RUNNABLE線程可運(yùn)行或正在運(yùn)行執(zhí)行了帝有超時(shí)參數(shù)的wait、TIMED_WAITINGTIMED_WAITsleep或join函數(shù)BLOCKEDMONITOR線程阻塞,等待獲取對(duì)象鎖執(zhí)行了洞時(shí)參數(shù)的wait函WAITINGWAIT數(shù)新建,正在初始化,為其分配NEWINITIALIZING資源NEWSTARTING新建,正在啟動(dòng)RUNNABLENATIVE正在執(zhí)行JNI本地函數(shù)WAITINGVMWAIT正在等待VM資源線程暫停,通常是由丁GC或RUNNABLESUSPENDEDdebug被暫停UNKNOWN未知狀態(tài)Thread.java中的狀態(tài)和Thread.cpp
42、中的狀態(tài)是有對(duì)應(yīng)關(guān)系的??梢钥吹角罢吒痈爬?,也比較容易理解,面向Java的使用者;而后者更詳細(xì),面向虛擬機(jī)內(nèi)部的環(huán)境。traces.txt中顯示的線程狀態(tài)都是Thread.cpp中定義的。另外,所有的線程都是遵循POSIX標(biāo)準(zhǔn)的本地線程。關(guān)于線程更多的說明可以查閱源碼/dalvik/vm/Thread.cpp中的說明。<!-線程的ThreadGroup最好也寫進(jìn)去->traces.txt文件中的這些信息是由每個(gè)Dalvik進(jìn)程的SignalCatcher線程輸出的,相關(guān)代碼可以查看/dalvik/vm/目錄下的SignalCatcher.cpp:logThreadStacks函數(shù)
43、和Thread.cpp:dvmDumpAllThreadsEx函數(shù)。另外請(qǐng)注意,輸出堆棧信息時(shí)SignalCatcher會(huì)暫停所有線程。通過該文件很容易就能知道問題進(jìn)程的主線程發(fā)生ANR寸正在執(zhí)行怎樣的操作。例如上述示例,ANRActivity在makeANI®數(shù)中執(zhí)行線程sleep時(shí)發(fā)生ANR可以推測(cè)sleep時(shí)間過長,超過了超時(shí)上限導(dǎo)致。這是一種比較簡(jiǎn)單的情況,實(shí)際開發(fā)中會(huì)遇到很多詭異的、更加復(fù)雜的情況,在后面的實(shí)例講解一節(jié)會(huì)詳細(xì)說明。1.2.5CPU使用率這部分的內(nèi)容主要涉及到Linux的相關(guān)知識(shí),數(shù)據(jù)是從“/proc/stat'文件中讀取的,Android中僅僅是匯總
44、和記錄這些數(shù)據(jù)而已,熟悉Linux的讀者可以跳過本節(jié)內(nèi)容。前面簡(jiǎn)單說明了CPU®用率信息,我們回顧一下,這次會(huì)有更多的知識(shí)點(diǎn)要說明。E/ActivityManager(127):ANRincom.example.anrdemo(com.example.anrdemo/.ANRActivity)E/ActivityManager(127):Reason:keyDispatchingTimedOutE/ActivityManager(127):Load:3.85/3.41/3.16/?CPU平均負(fù)載/AN收生之前的一段時(shí)間內(nèi)的CPUS用率E/ActivityManager(127):CP
45、Uusagefrom26835msto3662msagowith99%awake:/E/ActivityManager(127):9.4%98/mediaserver:9.4%user+0%kernelE/ActivityManager(127):8.9%127/system_server:6.9%user+2%kernel/faults:1823minor/minor或者major的貞錯(cuò)誤次數(shù).E/ActivityManager(127):/+0%5033/com.example.anrdemo:0%user+0%kernelE/ActivityManager(127):39%TOTAL:3
46、2%user+6.1%kernel/AN收生之后的一段時(shí)間內(nèi)的CPUS用率E/ActivityManager(127):CPUusagefrom601msto1132mslaterwith99%awake:E/ActivityManager(127):10%127/system_server:1.7%user+8.9%kernel/faults:5minorE/ActivityManager(127):10%163/InputDispatcher:1.7%user+8.9%kernelE/ActivityManager(127):1.7%127/system_server:1.7%user+0
47、%kernelE/ActivityManager(127):1.7%135/SurfaceFlinger:0%user+1.7%kernelE/ActivityManager(127):1.7%2814/BinderThread#:1.7%user+0%kernel.E/ActivityManager(127):37%TOTAL:27%user+9.2%kernel以上信息其實(shí)包含了兩個(gè)概念:CPU負(fù)載和CPU®用率,它們是不同的。不過負(fù)載的概念主要是做大型服務(wù)器端應(yīng)用時(shí)關(guān)注的性能指標(biāo),在Android開發(fā)中我們更加關(guān)注的是使用率。下面詳細(xì)說明,有八個(gè)關(guān)鍵點(diǎn)需要注意:CPU負(fù)載/平均
48、負(fù)載CPU載是指某一時(shí)刻系統(tǒng)中運(yùn)行隊(duì)列長度之和加上當(dāng)前正在CPU上運(yùn)行的進(jìn)程數(shù),而CPU¥均負(fù)載可以理解為一段時(shí)間內(nèi)正在使用和等待使用CPUW活動(dòng)進(jìn)程的平均數(shù)景。在Linux中“活動(dòng)進(jìn)程”是指當(dāng)前狀態(tài)為運(yùn)行或不可中斷阻塞的進(jìn)程。通常所說的負(fù)載其實(shí)就是指平均負(fù)載。用一個(gè)從網(wǎng)上看到的很生動(dòng)的例子來說明(不考慮CPU寸間片的限制),把設(shè)備中的一個(gè)單核CPUt匕作一個(gè)電話亭,把進(jìn)程比作正在使用和等待使用電話的人,假如有一個(gè)人正在打電話,有三個(gè)人在排隊(duì)等待,此刻電話亭的負(fù)載就是4。使用中會(huì)不斷的有人打完電話離開,也會(huì)不斷的有其他人排隊(duì)等待,為了得到一個(gè)有參考價(jià)值的負(fù)載值,可以規(guī)定每隔5秒記錄一
49、下電話亭的負(fù)載,并將某一時(shí)刻之前的一分鐘、五分鐘、十五分鐘的的負(fù)載情況分別求平均值,最終就得到了三個(gè)時(shí)段的平均負(fù)載。實(shí)際上我們通常關(guān)心的就是在某一時(shí)刻的前一分鐘、五分鐘、十五分鐘的CP坪均負(fù)載,例如以上日志中這三個(gè)值分別是3.85、3.41、3.16,說明前一分鐘內(nèi)正在使用和等待使用CPU勺活動(dòng)進(jìn)程平均有3.85個(gè),依此類推。在大型服務(wù)器端應(yīng)用中主要關(guān)注的是第五分鐘和第十五分鐘的兩個(gè)值,但是Android主要應(yīng)用在便攜手持設(shè)備中,有特殊的軟硬件環(huán)境和應(yīng)用場(chǎng)景,短時(shí)間內(nèi)的系統(tǒng)的較高負(fù)載就有可能造成ANR所以筆者認(rèn)為一分鐘內(nèi)的平均負(fù)載相對(duì)來說更具有參考價(jià)值。CPU勺負(fù)載和使用率沒有必然關(guān)系,有可能
50、只有一個(gè)進(jìn)程在使用CPU但執(zhí)行的是復(fù)雜的操作;也有可能等待和正在使用CPU勺進(jìn)程很多,但每個(gè)進(jìn)程執(zhí)行的都是簡(jiǎn)單操作。實(shí)際處理問題時(shí)偶爾會(huì)遇到由于平均負(fù)載高引起的ANR典型的特征就是系統(tǒng)中應(yīng)用進(jìn)程數(shù)最多,CPU總使用率較高,但是每個(gè)進(jìn)程的CPU®用率不高,當(dāng)前應(yīng)用進(jìn)程主線程沒有異常阻塞,一分鐘內(nèi)的CPUF均負(fù)載較高。提示:Linux內(nèi)核不斷進(jìn)行著CPU負(fù)載的記錄,我們可以在任意時(shí)刻通過在shell中執(zhí)行“cat/proc/loadavg”查看。ANR發(fā)生之前和之后一段時(shí)間的CPU®用率CPU®用率可以理解為一段時(shí)間(記作T)內(nèi)除CPLS閑時(shí)間(記作I)之外的時(shí)間與這
51、段時(shí)間T的比值,用公式表示可以寫為:CPU®用率=(T-I)/T而時(shí)間段T是兩個(gè)采樣時(shí)間點(diǎn)的時(shí)間差值。之所以可以這樣計(jì)算,是因?yàn)長inux內(nèi)核會(huì)把從系統(tǒng)啟動(dòng)開始到當(dāng)前時(shí)刻CP姑動(dòng)的所有時(shí)間信息都記錄下來,我們可以通過查看“/proc/stat文件獲取這些信息。主要包括以下幾種時(shí)間,這些時(shí)間都是從系統(tǒng)啟動(dòng)開始計(jì)算的,單位都是0.01秒:user:CPU在用戶態(tài)的運(yùn)行時(shí)間,不包括nice值為負(fù)數(shù)的進(jìn)程運(yùn)行的時(shí)間nice:CPU在用戶態(tài)并且nice值為負(fù)數(shù)的進(jìn)程運(yùn)行的時(shí)間system:CPUB內(nèi)核態(tài)運(yùn)行的時(shí)間idle:CPU空閑時(shí)間,不包括iowait時(shí)間iowait:CPU等待I/O操作
52、的時(shí)間irq:CPU硬中斷的時(shí)間softirq:CPUC中斷的時(shí)間注意:隨著Linux內(nèi)核版本的不同,包含的時(shí)間類型有可能不同,例如2.6.11中增加的stealstolen等。但根據(jù)Android源碼,我們只需要關(guān)心以上七種類型即可。CPL®用率的計(jì)算是在ProcessStats類中實(shí)現(xiàn)的。如果在某兩個(gè)時(shí)刻T1和T2(T1<T2)進(jìn)行采樣記錄,CPU®用率的整個(gè)算法可以歸納為以下幾個(gè)公式:userTime=(user2+nice2)-(user1+nice1)systemTime=system2-system1idleTime=idle2-idle1iowaitTi
53、me=iowait2-iowait1irqTime=irq2-irqlsoftirqTime=softirq2-softirqlTotalTime=userTime+systemTime+idleTime+iowaitTime+irqTime+softirqTime有了以上數(shù)據(jù)就可以計(jì)算具體的使用率了,例如用戶態(tài)CPU®用率為:userCpuUsage=userTime/TotalTime依此類推可以計(jì)算其他類型的使用率。而整個(gè)時(shí)間段內(nèi)CPU®用率為:CpuUsage=(TotalTime-idleTime)/TotalTime以上計(jì)算的是整個(gè)系統(tǒng)的CPLK用率,對(duì)于指定進(jìn)程的使用率是通過讀取該進(jìn)程的“/proc/進(jìn)程號(hào)/stat”文件計(jì)算的,而對(duì)于指定進(jìn)程的指定線程的使用率是通過讀取該線程的“/proc/進(jìn)程號(hào)/task/線程號(hào)/stat”文件計(jì)算的。進(jìn)程和線程的CPU®用率只包含該進(jìn)程或線程的總使用率、用戶態(tài)使用率和內(nèi)核態(tài)使用率。AM睢執(zhí)行appNotResponding函數(shù)過程中,共輸出了兩個(gè)時(shí)間段的CPU使用率,通常情
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 醫(yī)養(yǎng)結(jié)合養(yǎng)老產(chǎn)業(yè)的創(chuàng)業(yè)投資方向
- 中國智能電網(wǎng)終端設(shè)備芯片設(shè)計(jì)行業(yè)市場(chǎng)運(yùn)行動(dòng)態(tài)、市場(chǎng)監(jiān)測(cè)及投資發(fā)展?jié)摿Ψ治鰣?bào)告
- 2025年中國固態(tài)儲(chǔ)氫行業(yè)市場(chǎng)發(fā)展前景研究報(bào)告-智研咨詢發(fā)布
- 面向復(fù)雜系統(tǒng)的歸納邏輯研究-全面剖析
- 綠色消費(fèi)意識(shí)與電商營銷-全面剖析
- 新能源摩托車市場(chǎng)趨勢(shì)分析-全面剖析
- 航空運(yùn)動(dòng)用戶畫像與精準(zhǔn)營銷-全面剖析
- 知識(shí)產(chǎn)權(quán)歸屬協(xié)議書范文
- 房地產(chǎn)合作開發(fā)框架協(xié)議書二零二五年
- 勞務(wù)派遣服務(wù)合同書協(xié)議書二零二五年
- 2025年春季六下(小升初)家長會(huì) 課件
- 高壓線下房屋維修安全措施
- 2024國能神東煤炭集團(tuán)有限責(zé)任公司第二批系統(tǒng)內(nèi)招聘70人筆試參考題庫附帶答案詳解
- 《酒鬼酒集團(tuán)公司存在的財(cái)務(wù)風(fēng)險(xiǎn)及應(yīng)對(duì)策略研究案例報(bào)告》9600字
- 2025年工業(yè)園區(qū)年度工作計(jì)劃范文
- 2024-2025學(xué)年上海市浦東新區(qū)初三一模語文試卷(含答案)
- 企業(yè)創(chuàng)新韌性的驅(qū)動(dòng)路徑研究
- 2025年江蘇泰州市第四人民醫(yī)院招聘高層次人才15人歷年高頻重點(diǎn)提升(共500題)附帶答案詳解
- 二零二五年度跨境電商合伙人合作協(xié)議書3篇
- 知憲明法與憲同行課件-高一上學(xué)期憲法宣傳周主題班會(huì)
- UL489標(biāo)準(zhǔn)中文版-2019斷路器UL標(biāo)準(zhǔn)中文版
評(píng)論
0/150
提交評(píng)論