您当前的位置:首页 > 手机百科 > 安卓百科

Android锁屏解锁卡顿优化——原理分析与GC回收

时间:2022-07-06 13:47:14  来源:  作者:Android秃老师

一、问题描述

我在使用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性能优化知识学习,获~可私信发送:“核心笔记”或“手册”即可获取!

五、文末

成功优化了大约3秒的时间,虽然还感觉有卡顿,但是已经好很多,因为为了方便调试,编译的是userdebug版本,使用user版本后效果还有提升,算是能够过的去了!

更多性能优化(卡顿优化、UI优化、布局优化、稳定性优化、电量优化等)可前往私信哦。



Tags:Android锁屏   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Android锁屏解锁卡顿优化——原理分析与GC回收
一、问题描述我在使用Android手机,解锁屏幕时出现了卡顿现象,从解锁到菜单页面需要卡顿3~4秒。对于这一方面,从事Android开发的我决定,测试分析解决这一问题。 二、分析问题我们...【详细内容】
2022-07-06  Search: Android锁屏  点击:(279)  评论:(0)  加入收藏
Android锁屏时或将显示广告,谷歌似乎无意阻拦
财经网科技7月6日讯,据新浪科技消息,Glance 公司是一家得到谷歌支持的移动广告公司。未来 2 个月,Glance 准备在美国将自己的锁屏平台推向 Android 设备,也就是说 Android 手机...【详细内容】
2022-07-06  Search: Android锁屏  点击:(308)  评论:(0)  加入收藏
▌简易百科推荐
安卓手机内存满了,如何清理内存?
随着安卓手机的普及,越来越多的人开始使用安卓系统。然而,由于安卓系统的开放性和可定制性,许多用户可能会在手机上安装各种各样的应用程序,导致手机内存被占用,从而影响手机的运...【详细内容】
2024-01-11      Tags:清理内存   点击:(98)  评论:(0)  加入收藏
如何释放安卓应用内存:清理优化指南
清理安卓应用的内存占用可以提高设备性能并释放空间。以下是详细的步骤和方法:关闭不必要的应用: 进入设备的“最近使用的应用”或按下多任务切换按钮(通常是右下角的方形按钮)...【详细内容】
2024-01-11  智盼游    Tags:安卓   点击:(53)  评论:(0)  加入收藏
安卓系统更新迭代与Root需求的变化
随着安卓系统的不断更新迭代,我们见证了整个生态从混乱到规范的过程。回溯过去,安卓生态处于一个“妖魔横行”的时代,各种弹窗广告、自动下载、自动启动等问题层出不穷。为了解...【详细内容】
2024-01-03  科技办公达人莱说    Tags:安卓系统   点击:(51)  评论:(0)  加入收藏
Android 谷歌三件套:解锁谷歌生态!
大家是不是遇到这个情况?当我们需要下载一些国外的游戏或者软件的时候,需要在手机里面安装Google Play商店,然后通过Google Play商店下载国外软件!为了帮助大家使用上各种好用的...【详细内容】
2024-01-02  MatrixWave  今日头条  Tags:Android   点击:(117)  评论:(0)  加入收藏
安卓手机上面有哪些好用的录音软件?
安卓手机上面有哪些好用的录音软件?在日常的生活中,很少需要通过录音记录,在录音时,尽量选择一个安静的环境进行录音,并将手机放在靠近声源的位置,以获得更好的录音效果。录制人声...【详细内容】
2023-12-29  美食得爱好者    Tags:录音软件   点击:(81)  评论:(0)  加入收藏
安卓手机的照片怎么传到苹果手机?试试这3个方法!
原本一直使用安卓手机,但今天新买了一部苹果手机,需要将安卓手机中的照片全部转移到新的苹果手机里,不过她对该如何操作感到困惑。购置新手机是一件令人兴奋的事情,而将旧手机的...【详细内容】
2023-12-29  艾尚去旅行    Tags:安卓手机   点击:(54)  评论:(0)  加入收藏
搞定流畅性过后,安卓旗舰手机游戏体验还要什么?
手机游戏可以改善体验的地方数不胜数,在骁龙移动平台原生支持的“超分、超帧”等技术支持下,未来手机必将以更低的成本,呈现出更出色的游戏体验。曾几何时,手机性能的展示被局限...【详细内容】
2023-12-20    PChome  Tags:安卓   点击:(47)  评论:(0)  加入收藏
我的手机我做主,如何为Android手机应用换图标?
作为一名Android用户,你是否曾经为自己的手机桌面感到单调而乏味?虽然Android系统的桌面定制性已经非常强大,但有时候我们还是希望能够在细节上做出一些改变,尤其是对于那些每天...【详细内容】
2023-12-10  王炸无人机飞手    Tags:Android   点击:(62)  评论:(0)  加入收藏
安卓手机低电量模式有什么影响,性能与续航的权衡
在现代社会,手机已经成为我们日常生活中不可或缺的一部分。然而,随着手机功能的不断增加,电池的续航时间逐渐成为一个令人关注的问题。为了解决这一问题,安卓手机提供了一种称为...【详细内容】
2023-11-11  PC小教程    Tags:安卓手机   点击:(111)  评论:(0)  加入收藏
谷歌安卓 14 减少后台活动,可提高性能、延长续航
IT之家 10 月 25 日消息,在最新一期 #TheAndroidShow 活动,谷歌的安卓工程副总裁 Dave Burke 表示,安卓 14 系统更改了处理器和内存的工作负载平衡机制,不仅可以提升设备性能,而...【详细内容】
2023-10-25    IT之家  Tags:安卓   点击:(116)  评论:(0)  加入收藏
站内最新
站内热门
站内头条