日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一、問題描述

我在使用Android手機,解鎖屏幕時出現了卡頓現象,從解鎖到菜單頁面需要卡頓3~4秒。對于這一方面,從事Android開發的我決定,測試分析解決這一問題。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

二、分析問題

我們使用systrace,我這里是用的ddms錄制trace.html,用chrom打開systrace提供了一些UI Thread耗時cpu耗時等分析圖,從圖上分析可以看出,有很長一段時間,系統在進行GC,大約花費3~4s的時間,感覺可以查一下問題,從網上查詢資料,知道是系統進行GC的時候是會阻塞當面界面的,表現上就是界面卡住了,但是GC是虛擬機系統行為,code無法控制,還得需要找源頭,為何會有這么多垃圾需要回收。

三、GC回收原理與解析

那我們就先來搞懂GC的源頭

GC原理

將內存中不再被使用的對象進行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,JAVA在對對象的生命周期特征進行分析后,按照新生代、舊生代的方式來對對象進行收集,以盡可能的縮短GC對應用造成的暫停。

常見GC算法

  • 引用計數
  • 標記清除
  • 標記整理
  • 分待回收(V8用到的)

算法解析

1、引用計數法(Reference Counting):

在對象中添加一個引用計數器,每當一個地方引用它時,計數器就加1;當引用失效時,計數器就減1;當引用計數為0時就會被回收。但是它存在一個很大的問題就是循環引用:如下圖,當實例化A時,A會持有實例B,B會持有C,C持有A。這樣一來我們就會發現:如果需要回收A,除了釋放初始實例化引用,還需要釋放C的引用。但是由于ABC互相引用,所以就造成誰也無法釋放。主流的垃圾回收都沒有采用這種判斷方法,因為需要額外的工作來解決它(感興趣的童鞋可以看看智能指針)。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

“”

可達性分析算法(Reachability Analysis):

在JAVA虛擬機中就是通過可達性分析法來判定對象是否存活的。思路是通過“GC Roots”的對象(可以認為是確定固定存在的對象)作為起始點,然后從這些節點開始遍歷所有引用鏈,如果某個對象沒有GC Roots直接或間接的連接的話,這個對象(白色節點)就被認為程序中不再使用可以被回收了。如下圖:

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

“”

代碼示例:

const user1 = { age: 11 }
const user2 = { age: 12 }
const user3 = { age: 13 }
const nameList = [user1.age, user2.age, user3.age]

function fn() {
  const num1 = 1
  const num2 = 2
  num3 = 3
}

fn()

當函數調用過后,num1和num2在外部不能使用,引用數為0,會被回收 ; num3是掛載在window上的,所以不會被回收 ; 上面的user1、user2、user3被nameList引用,所以引用數不為0不會被回收 ;

上面無法回收循環應用對象舉例:

function fn() {
  const obj1 = {}
  const obj2 = {}
  obj1.name = obj2
  obj2.name = obj1
  return 'hello world'
}

fn()
// obj1和obj2,因為互相有引用,所以計數器并不為0,fn調用之后依舊無法回收這兩個對象

2、標記清除:

其分為“標記”和“清除”兩個階段。首先標記出所有死亡的對象,然后把所有死亡的的對象進行清除操作。如下圖,我們可以清楚地看到,這種回收算法有一個很大的問題:造成很多的不連續內存碎片,這樣一來,如果需要創建稍微大一點的對象,就很可能無法找到足夠大的內存空間。這就需要整個再進行一次標記整理來解決這一問題(耗時耗力)。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

3.標記整理:

算法分為”標記-整理-清除“階段,首先需要先標記出存活的對象,然后把他們整理到一邊,最后把存活邊界外的內存空間都清除一遍。這個算法的好處就是不會產生內存碎片,但是由于整理階段移動了對象,所以需要更新對象的引用。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

4.標記復制:

算法分標記-復制兩個階段。首先會標記存活的對象,完成后,該算法會把存活的對象都復制到一塊新的空內存里去。最后將原來的內存空間清空。過程如下圖,這個算法最大的問題就是需要很大的內存(實際用地,用于復制的內存空間),同時如果存活的對象非常多的話,標記和復制階段都就會很慢。同時也涉及到了對象位置改變需要更新引用。盡管看起來問題很大,但是根據分代理論:弱分代假說里大多數對象生命周期短,這種情況下標記復制就很適合了(復制的存活對象少)。至于內存消耗太大的問題,java虛擬機通過將新生代分為一個Eden區與2個Survivo區,其中一個Survivo區用來復制,這樣一來極大地提高了內存空間利用率。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

了解到GC的原理以及他的算法,我們就來看看如何解決問題。

四、解決方案

打開著名的traceview工具,也是分析性能問題的好幫手。同樣使用DDMS工具上集成的方式,我使用的是MTK release的工具GAT首先我用traceView看了一下,traceview主要是看一些方法的耗時和調用情況,以及消耗cpu的狀態,從函數方法的調用來看,耗時最高的就是主線程,達到了4.7秒,圖形上看,似乎這個Binder操作耗費的時間有點高。

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

雙擊這一塊看到信息,Binder操作的inc cpu time占用14%,但是incl Real Time是73%的時間,達到了5秒多,這種情況主要是因為可能CPU的上下文切換、阻塞、GC等原因造成,與systrace上看出的問題一致,如圖:

Android鎖屏解鎖卡頓優化——原理分析與GC回收

 

下面再錄制一份log,找關鍵字ActivityManager和Binder看看情況,找到如下log:

01-21 14:07:49.951 1109 1285 I ActivityManager: Displayed com.android.settings/.SubSettings: +5s544ms [aosp]

可見,這個subSetting花了5秒半,相當長,循著時間往前看5s,看看是否有什么蛛絲馬跡,找到了binder出錯的信息:

01-21 14:07:43.931 4218 4218 E ActivityThread: Activity com.android.settings.SubSettings has leaked ServiceConnection com.android.settings.password.ChooseLockGeneric$ChooseLockGenericFragment1 @ 680 f 7 e 9 t h a t w a s o r i g i n a l l y b o u n d h e r e 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a n d r o i d . a p p . S e r v i c e C o n n e c t i o n L e a k e d : A c t i v i t y c o m . a n d r o i d . s e t t i n g s . S u b S e t t i n g s h a s l e a k e d S e r v i c e C o n n e c t i o n c o m . a n d r o i d . s e t t i n g s . p a s s w o r d . C h o o s e L o c k G e n e r i c 1@680f7e9 that was originally bound here 01-21 14:07:43.931 4218 4218 E ActivityThread: android.App.ServiceConnectionLeaked: Activity com.android.settings.SubSettings has leaked ServiceConnection com.android.settings.password.ChooseLockGeneric1@680f7e9thatwasoriginallyboundhere01−2114:07:43.93142184218EActivityThread:android.app.ServiceConnectionLeaked:Activitycom.android.settings.SubSettingshasleakedServiceConnectioncom.android.settings.password.ChooseLockGenericChooseLockGenericFragment1 @ 680 f 7 e 9 t h a t w a s o r i g i n a l l y b o u n d h e r e 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t a n d r o i d . a p p . L o a d e d A p k 1@680f7e9 that was originally bound here 01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.LoadedApk1@680f7e9thatwasoriginallyboundhere01−2114:07:43.93142184218EActivityThread:atandroid.app.LoadedApkServiceDispatcher.(LoadedApk.java:1532)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:1424)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1605)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ContextImpl.bindService(ContextImpl.java:1557)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.content.ContextWrapper.bindService(ContextWrapper.java:684)
01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.settings.password.ChooseLockGenericC h o o s e L o c k G e n e r i c F r a g m e n t . b i n d S e r v i c e ( C h o o s e L o c k G e n e r i c . j a v a : 297 ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t c o m . a n d r o i d . s e t t i n g s . p a s s w o r d . C h o o s e L o c k G e n e r i c ChooseLockGenericFragment.bindService(ChooseLockGeneric.java:297) 01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.settings.password.ChooseLockGenericChooseLockGenericFragment.bindService(ChooseLockGeneric.java:297)01−2114:07:43.93142184218EActivityThread:atcom.android.settings.password.ChooseLockGenericChooseLockGenericFragment.onCreate(ChooseLockGeneric.java:201)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.Fragment.performCreate(Fragment.java:2489)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1237)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.addAddedFragments(FragmentManager.java:2407)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2186)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2142)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2043)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:799)
01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.settings.SettingsActivity.switchToFragment(SettingsActivity.java:781)
01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.settings.SettingsActivity.launchSettingFragment(SettingsActivity.java:439)
01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.settings.SettingsActivity.onCreate(SettingsActivity.java:327)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.Activity.performCreate(Activity.java:7023)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.Activity.performCreate(Activity.java:7014)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2734)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2859)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ActivityThread.-wrap11(Unknown Source:0)
01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ActivityThreadH . h a n d l e M e s s a g e ( A c t i v i t y T h r e a d . j a v a : 1592 ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t a n d r o i d . o s . H a n d l e r . d i s p a t c h M e s s a g e ( H a n d l e r . j a v a : 106 ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t a n d r o i d . o s . L o o p e r . l o o p ( L o o p e r . j a v a : 164 ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t a n d r o i d . a p p . A c t i v i t y T h r e a d . m a i n ( A c t i v i t y T h r e a d . j a v a : 6518 ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( N a t i v e M e t h o d ) 01 − 2114 : 07 : 43.93142184218 E A c t i v i t y T h r e a d : a t c o m . a n d r o i d . i n t e r n a l . o s . R u n t i m e I n i t H.handleMessage(ActivityThread.java:1592) 01-21 14:07:43.931 4218 4218 E ActivityThread: at android.os.Handler.dispatchMessage(Handler.java:106) 01-21 14:07:43.931 4218 4218 E ActivityThread: at android.os.Looper.loop(Looper.java:164) 01-21 14:07:43.931 4218 4218 E ActivityThread: at android.app.ActivityThread.main(ActivityThread.java:6518) 01-21 14:07:43.931 4218 4218 E ActivityThread: at java.lang.reflect.Method.invoke(Native Method) 01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.internal.os.RuntimeInitH.handleMessage(ActivityThread.java:1592)01−2114:07:43.93142184218EActivityThread:atandroid.os.Handler.dispatchMessage(Handler.java:106)01−2114:07:43.93142184218EActivityThread:atandroid.os.Looper.loop(Looper.java:164)01−2114:07:43.93142184218EActivityThread:atandroid.app.ActivityThread.main(ActivityThread.java:6518)01−2114:07:43.93142184218EActivityThread:atjava.lang.reflect.Method.invoke(NativeMethod)01−2114:07:43.93142184218EActivityThread:atcom.android.internal.os.RuntimeInitMethodAndArgsCaller.run(RuntimeInit.java:438)
01-21 14:07:43.931 4218 4218 E ActivityThread: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
01-21 14:07:43.987 1109 3345 W ActivityManager: Unbind failed: could not find connection for android.os.BinderProxy@8d5f5b4

好巧,報錯也是因為Binder沒有unBinder成功,與traceview的Binder耗時,以及systrace的GC耗時的信息都有千絲萬縷的關系,也許就是問題的所在,接下來就是解決這個錯誤,并驗證的時刻。找到如下資料:

google Android Issue中有這個缺陷,缺陷詳細信息在這里(Google Android Issue 2483),
Using getApplicationContext().bindService instead of just bindService on your activity solves the problem as it is using the higher level application context.
先調用getApplicationContext()獲取其所屬的Activity的上下文環境才能正常bindService,
也就是在onCreate()方法中使用this.getApplicationContext().bindService([args…])就可以了,否則bindService將永遠失敗返回false。

那么就看log找到對應的地方,我們發現綁定binder的地方是用的getContext().Binder(xxxx),解綁的地方是用的getActivity().unBinder(xxx),我們通通換成getActivity().getApplicationContext().binder(xxx) (unBinder),編譯push驗證,查看log如下:

Line 2240: 01-21 15:29:12.629 1167 1325 I ActivityManager: Displayed com.android.settings/.SubSettings: +2s672ms [aosp]

Android性能優化知識學習,獲~可私信發送:“核心筆記”或“手冊”即可獲?。?/strong>

五、文末

成功優化了大約3秒的時間,雖然還感覺有卡頓,但是已經好很多,因為為了方便調試,編譯的是userdebug版本,使用user版本后效果還有提升,算是能夠過的去了!

更多性能優化(卡頓優化、UI優化、布局優化、穩定性優化、電量優化等)可前往私信哦。

分享到:
標簽:Android 鎖屏
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定