| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Android R常见GC类型与问题案例 -> 正文阅读 |
|
[移动开发]Android R常见GC类型与问题案例 |
前言 Android系统的APP运行需要依赖ART虚拟机(Android Runtime),ART虚拟机的主要作用是给APP的java代码提供运行环境,其中编译、执行、垃圾回收(GC)模块是ART虚拟机的重中之重。GC使得java开发人员能专注于业务实现,而不用担心内存泄漏。 此文章将简要的向大家介绍ART虚拟机中Heap布局、常见GC类型和对应的问题案例。为大家分析优化应用提供一些思路。 本文基于的代码和调试手机系统为Android R(11)版本。 一、GC的相关配置 1. 内存回收器(回收算法)、内存分配器 因为Android R支持读屏障(kUseReadBarrier),在虚拟机创建阶段,内存回收器设置为: *前台回收器foreground_collector_type = kCollectorTypeCC *后台回收器background_collector_type = kCollectorTypeCCBackground ##对于CC(ConcurrentCopying)并发复制回收器而言,前后台回收器只用于配置前后台GC时的一些回收器参数,实质回收器只有一种即kCollectorTypeCC。一般当应用退到后台,系统希望应用尽可能释放UI相关的垃圾对象并最大程度的进行内存整理。 ##读屏障(kUseReadBarrier)有三种实现方式,涉及到内存回收算法的底层实现逻辑,读者可自行了解,本文不做阐述。 ART虚拟机创建时先确定内存回收器的类型,进而绑定对应的内存分配器,因回收器已设置为CC(ConcurrentCopying)即并发复制回收器,则Heap内存的主要分配区域定为RegionSpace,RegionSpace由一个个256KB的Region组成。对应的RegionSpace内存分配器定为kAllocatorTypeRegion。 从下图代码中可知,并发复制回收若启用分代GC,默认回收策略是Sticky即只回收上次GC后新生成的对象。若未启用分代,默认回收策略是Full即扫描所有space包括zygoteSpace的对象做回收。Android R版已启用分代,则默认策略模式是Sticky。 二、ART虚拟机Heap内存布局 1. 应用Heap布局示例 作者使用Android R版本手机,安装HelloWorld应用查看进程map文件,应用的Heap布局如下图。 此手机Heap参数和应用large_heap配置如下,应用可用堆上限为256MB。 [dalvik.vm.heapgrowthlimit]: [256m] [dalvik.vm.heapsize]: [512m] android:largeHeap="false" 2. Space类型 (1)RegionSpace ?[512M]: A) 虚拟地址起点为300MB(0x12c00000),可看到图中和ImageSpace之间地址不连续。 B) 虚拟地址范围:为支持堆完整复制,需要额外预留一倍空间。当进程启动后BindApplication阶段调用ClampGrowthLimit()将RegionSpace 虚拟地址空间size设置为两倍的堆上限。 C) 内存分配:绝大多数的对象分配的区域。RegionSpace由256KB大小的Region组成,Region的分配算法是最简单的指针碰撞(BumpPointer)。 当一次分配请求到来,通过For循环遍历可用的Region。当已分配的Region数量已超过总数的一半,不允许再占用新的Region,只能依赖内存回收和整理来释放内存用于分配。 D) 回收策略:当一个Region中存活对象占用大小超过75%时,此Region标记为kRegionTypeUnevacFromSpace,表示无需进行拷贝。否则将此Region中存活对象拷贝至标记成ToSpace的另一个Region中,拷贝完成后回收整个Region,立刻释放256KB内存。 具体的算法实现更加复杂,读者可进一步了解CC算法的实现。 (2) ImageSpace ?[4M]: A) Zygote进程创建时,根据boot.art文件ImageHeader结构体中指定的映射地址(0x70e44000),创建ImageSpace,将boot.art内存镜像文件映射到进程的虚拟地址空间。 B) ImageSpace不支持分配对象,也无需回收。 C) ImageSpace创建后同步映射boot.oat文件到ImageSpace地址后面。 D) ImageSpace中映射的.art文件可能有多个,比如boot-core-libart.art/boot-okhttp.art。boot.oat区域亦然。 (3)ZygoteSpace ?[3M]: A)包含Zygote进程从创建到第一次Fork前所有存活的对象。当Fork新进程时无需再单独映射或创建必需的对象,直接复用Zygote进程的数据,加快进程创建。 B)创建过程: 1 Zygote进程创建Heap时,申请一块DlmallocSpace类型的64MB地址空间,命名为“zygote/non moving space”。 2 Zygote进程此时可在“zygote/non moving space”和RegionSpace中分配对象。 3 当Fork SystemServer时,Zygote进程进行一次Full GC并Trim裁剪内存,将回收后的内存还给系统。再将RegionSpace中存活对象复制到“zygote/non moving space”,将两部分的存活对象进行一次压缩。整个64MB空间一拆为二,压缩后保留Zygote进程所有存活对象的Space命名为“zygote space”,剩余的空闲内存命名为“non moving space”。 C)一拆为二后,新的“zygote space”不再支持内存分配与释放。 (4)Non moving space ?[61M]: A)?分配算法:Dlmalloc。 B)?存放AllocNonMovableObject分配的obj,主要是类加载过程创建的类对象(Class)、类方法对象(ArtMethod)、类成员变量对象(ArtField)。 C)?创建过程见2.2.3。 (5) LOS-LargeObjectSpace ?[512M]: A) LOS有两种实现类型(free list\mmap),Android R默认实现为free list形式,以Heap size(capacity_)作为参数调用FreeListSpace::Create()接口创建。 B)?单次分配超过12KB的String和基础类型的数组如int[],在LOS中分配。 三、GC的常见类型 1. GC流程简介 GC相关博客文章已经浩如烟海,推荐读者阅读邓凡平老师的《深入理解Android:java虚拟机ART》、周志明老师的《深入理解java虚拟机》和网上的博客例如罗升阳、芦航等,本文不做铺陈。 2. GC类型与案例 为了反映不同场景下的GC,作者使用GC对应用状态的影响和gc_cause(触发原因)来做区分。能够更好的阐述GC不同类型之间的异同。 基于GC对应用状态的影响将GC初步分为两类,并发类和阻塞类。并发类GC指GC在GC回收线程(HeapTaskDaemon)执行,阻塞类GC在进程的工作线程执行。 再根据gc_cause具体分为Background GC\Native GC\CollectorTransition GC\Alloc GC\Explicit GC。 (1) 并发类GC 并发类GC因为运行在HeapTaskDaemon线程中,对应用的状态影响较小,一般情况下对于用户体验和应用逻辑是透明的。并发类GC对于内存回收具有至关重要的作用,减少系统处于低内存和大量内存碎片的情况。对于如今的手机SOC算力和配置来说,CPU能力比较富余,而内存不太富余,应当在合适的时机多执行并发类GC。 但是在一些特殊场景如某些特殊应用、低端手机中,因并发类GC带来的卡顿、高负载、功耗问题也很常见,主要的影响有以下几类。 (A) HeapTaskDaemon运行GC时,CC算法需要很强算力,容易抢占CPU大核\超大核资源,导致应用UI绘制相关线程task无法及时被CPU调度或UI绘制相关线程被挤在小核中无法在一个Vsync周期完成绘制与渲染。进而引起卡顿不流畅。 (B)?后台驻留应用多,且后台活动频繁时,多个后台HeapTaskDaemon占用多个CPU核,加重负载情况,引起整机高负载,在整机高负载下各进程的线程存在各种Runnable和Uninterruptable sleep(D)状态,各种SystemServer等锁、ANR、IOwait、BlockMsg情形接踵而至,卡顿也就逃不掉了。 (C) CC回收算法STW(stop the world)时间即pause time已经非常低,但是依然存在一些场景下,因CC算法某些阶段需要STW(比如ReclaimPhase阶段需要LockHeap锁堆,锁堆会STW),造成进程除HeapTaskDaemon外的其他线程都处于Sleeping状态,特殊应用可能出现STW时间达到几十ms级别以上,会导致卡顿问题。见3.2.1.1.1点。 (D) Blocking GC导致丢帧,例如主线程调用System.gc()进行一次显示GC,因ART虚拟机一次只能发起一次GC,若此时有其他GC在运行,需等待此次GC完成,主线程一直等待导致丢帧卡顿。 (E)?特殊应用在用户操作时临时对象分配内存过多,HeapTaskDaemon持续进行GC,GC频繁导致场景功耗电流很高,影响续航与发热。 ① Background GC/Bg GC 此类为最常见的GC类型,大致90%以上的GC都是此类型。 (A)?触发逻辑:ART 中heap.cc维护一个Background GC水位值(concurrent_start_bytes_),此水位值由以下因素(multiplier\maxfree\minfree\utilization\gctype)共同影响。在每次GC完成后,调用GrowForUtilization()接口计算Background GC水位值。 ???? GC水位计算比较简单,读者可百度GC触发时机相关文章,本文不再铺陈。 在每次对象分配后,判断已分配java堆大小和水位的对比,若超水位立刻触发Background GC。 (B) 作用:时刻追踪java堆大小,避免垃圾对象过多,及时回收内存,减少不必要的内存消耗。 1) 案例1【前台应用Bg GC频繁,引起主观滑动卡顿,顿挫感明显】 应用:新浪新闻V7.63.1 场景:视频>>小视频>>短视频展示页,上下滑动,明显卡顿 systrace情形:HeapSize呈锯齿状,HeapTaskDaemon中GC频繁,Sticky/Full GC互相交替运行。 卡顿根因:滑动过程此应用临时对象占用内存过多,堆Heap size上升迅速,达到GC水位线,触发Bg GC。GC回收ReclaimPhase阶段lock heap锁堆各线程被pause,主线程Sleeping耗时过长丢帧卡顿。 优化方向:应用内存使用不合理,反馈应用优化。 2) 案例2【后台应用Bg GC频繁,引起整机高负载,引起前台应用卡顿】 场景:后台驻留20-30个应用,压力测试应用启动和关闭时桌面动画流畅性 Systrace情形:后台应用活动频繁,多个进程HeapTaskDaemon进程占用卡顿时间段内30-50% CPU资源。 卡顿根因:后台应用活动频繁,引起整机高负载,引起前台应用的UI相关线程资源调度不及时。 优化方向:1 排查后台管控策略,加强管控能力 2 缓解方案:减少高负载时Bg GC,优化HeapTaskDaemon调度 ② Native GC/NativeAlloc GC ART可以管理一些特定的Native内存。某些java类(如Bitmap)使用NativeAllocationRegistry类申请和释放Native内存时会同步告知ART,ART可监控此类由java对象引用的Native对象内存。 此类引用模式就像“提线木偶”,java对象引用Native对象,java对象内存占用很小,大头在Native对象。在ART回收java对象时,此java对象已设置死亡回调会主动释放引用的Native内存。 基于此原理ART虚拟机可间接管理一部分Native内存。 ##注意,开发人员在C++\C代码中malloc\mmap分配的Native内存,无法被ART管理,此部分内存使用没有上限,只能依靠LMKD或虚拟地址空间耗尽OOM,进程死亡后回收。 (A)?触发逻辑: 1 单次超过300KB的Native内存分配,触发Native已分配内存和Native GC水位检查,超过则进行一次NativeGC。 2 每32次小于300KB的Native内存分配,触发一次检查,超过Native GC水位进行一次NativeGC。 (B)?与Background GC异同点:GC代码流程完全一样,主要为触发原因不同。Bg GC回收Bitmap,也会自动释放Bitmap关联的Native内存。 ③?案例1【Native GC频繁引起特定场景高负载】 场景:应用内退出到桌面、特定应用(如相机、相册)冷热启动 Systrace表现:应用进程HeapTaskDaemon线程运行gc_cause为NativeAlloc的GC,增加系统负载。 优化方向:simplePerf确认触发堆栈,推动优化,若为超过300KB的NativeGC,强烈建议优化掉,减少必现的GC。 ④?CollectorTransition GC 此类GC发生在应用processState在IMPORTANT_FOREGROUND(6)状态上下切换时,processState<=6代表应用可被感知,>6代表应用活动不被感知。ART中只关心是否能被感知,进而调整一些GC的参数。在ART设计中,应用processState>6时,代表应用退入后台用户无法感知(STW时间不关心),此时应进行内存回收和整理,减少内存碎片,提升内存利用率。CollectorTransition GC就是起此作用。 (A)?触发逻辑:应用退入后台即processState>6,代表用户无法感知此应用,请求一次delay 5s的并发GC,gc_cause为kGcCauseCollectorTransition。 (B)?与Background GC异同点:GC逻辑流程一致,但是CC回收器对Explicit GC和CollectorTransition GC会进行最大程度的内存整理,所有region会被拷贝至tospace,不受75%比例的限制,存活对象整理到新的region中。 ⑤?案例1【CollectorTransition GC频繁引起应用退出动画卡顿】 应用:腾讯视频 场景:后台驻留20-30个应用,压力测试应用开启与关闭时的流畅性 Systrace表现:桌面启动应用时动画不流畅,整机负载偏高,Heaptaskdaemon为负载TOP1,主要占据4个小核。 卡顿根因:GC负载主因是腾讯视频退后台,延迟5S后触发CollectorTransition类型的GC,且腾讯视频同个UID下5个进程同步触发。刚好遇上桌面启动应用过程。另外其他应用的GC也有一定负载影响。 优化方向:1 加强后台进程管控(退后台快速冻结,限制资源使用等)2 保证内存压缩效果情况下减少CollectorTransition GC。 (2) 阻塞类GC 作者将运行在工作线程中的GC归类为阻塞类GC,主要有两类:Alloc GC和Explicit GC,阻塞类GC会Block工作线程,可能出现GC耗时过长,导致进程出现操作卡顿、ANR、卡死等情况发生。在应用设计中,应尽可能避免此类GC。 Alloc GC常见于进程heap size触顶即达到堆上限,无法再继续分配对象,将发起一次或多次GC回收内存再尝试分配,若依然分配失败,会触发OOM进程死亡。 Explicit GC常见于两种情况,1 应用代码中主动调用Runtime.gc()\System.gc(),主要见于APP覆写onTrimMemory()接口。2 SystemServer\系统应用\adb调试等进程给对应进程发送kill -10产生SIGUSER1的signal,此进程的SignalCatcher线程捕获signal后执行一次Explicit GC。 阻塞类GC主要影响有以下几点: (A)?阻塞工作线程运行,GC耗时普遍在50ms-200ms间甚至更长,APP逻辑运行时间增加,可能引起卡顿问题。 (B)?应用主动频繁调用Explicit GC,可能出现回收效果甚微,但增加大量无效负载,引起功耗增加,续航减少,手机发热等问题。 (C)?在进程heap size快要触顶达到堆上限时,一般是内存泄漏场景,此时各工作线程和HeaptaskDaemon都在发起各种GC,各类GC互相Block等锁,导致应用卡死。头部TOP应用比较常见。 ① Alloc GC 从项目经验看,因为最近几年的Android版本都使用CC回收器,RegionSpace的limit已经设置成堆上限值,出现Alloc GC即可代表heap size接近触顶快到达堆上限,几乎无法回收出内存。 绝大部分都是应用出现内存泄漏。此时多次Alloc GC也释放不出内存,出现各种Block等锁,应用卡死,挤牙膏挤不出,或者只能挤一点点,等待此进程的要不卡死要不就是OOM。 以下两个案例介绍这种情形。 1) 案例1【Alloc GC频繁引起应用操作卡顿】 应用:内部测试工具 场景:测试工具持续压力使用,应用长时间卡顿定屏 Systrace表现:如下图,heapsize已经达到242M,此时分配大内存对象失败,主线程触发多次Alloc GC。因内存泄漏,并未回收出内存,应用马上出现OOM闪退。 卡顿根因:应用内存泄漏。 优化方向:1 解决内存泄漏点 2 若应用必须占用大量内存,可配置android:largeHeap="true" 3 fork新进程实现功能,避免单个进程heap size过高 2)?案例2【Block GC频繁引起应用卡死】 应用:抖音 场景:长时间压力测试抖音视频切换 问题特征: 1 Log:卡死期间一直打印Starting a blocking GC xx和WaitForGcToComplete blocked xx on xx for xxms 2 Systrace:Heap size触顶,且各线程有很多TAG(GC:Wait For Completion xx) 3 Meminfo:进程此时内存占用2G以上 卡死根因:抖音内存泄漏,各工作线程发起Alloc GC,但HeapTaskDaemon正在运行GC,各工作线程等锁Sleeping,造成卡死。 优化方向:推动应用优化内存泄漏(已反馈,新版本已优化) ② Explicit GC/显式GC/强制GC/ 见3.2.2阻塞类GC的阐述,此类GC多见于应用主动调用Runtime.gc()\System.gc(),以下两个案例分别为OnTrimMemory调用显式GC和工作线程中主动调用显式GC。 ##Explicit GC和CollectorTransition GC一样,没有MakingPhase阶段,因为系统设定两种GC都会做最大力度的内存整理,所有region的存活对象都会被拷贝,不受75%存活量占比限制。 1)案例1【OnTrimMemory 中Explicit GC频繁引起应用卡顿】 应用:平行空间、bima+ 场景:后台驻留35应用,自动化用例运行24小时,检查各进程丢帧与系统各指标 特征: systrace文件中发现doFrame的commit callback中执行trimMemory,执行一次concurrent copying GC。导致此次doFrame耗时过长,丢帧卡顿。 根因: 系统低内存触发OnTrimMemory回调,应用在OnTrimMemory中直接调用显式GC接口。 优化方向:1 系统分析优化低内存场景 2 应用实现OnTrimMemory因根据trimLevel和自身应用状态,根据情况按需调用显式GC接口,并且调用操作在子线程中实现。 2) 案例2【微信视频号/视频直播页面Explicit GC频繁】 应用:微信 V8.0.11 场景: 1 发现》直播和附近》直播》任意打开一个直播》上下滑动切换,必现一次显式GC 2 发现》点击视频号,必现一次显式GC 3 点击好友头像》进入好友详情页,点击好友的视频号,必现一次显式GC 优化方向:google已明确不建议应用调用显式GC来回收内存,Bg GC已经可以很好的回收垃圾对象,应用逻辑必现的显式GC,应优化内存分配逻辑,去除显式GC调用。减少无效负载。 当前此问题正和微信对接中。 四、总结 本文从Android R版本GC相关配置为入口,以HelloWorld应用为例介绍Heap布局。能够让读者对Android R版本java内存分配和管理有一个概念认识。紧接着以实际GC类型和案例,介绍各种不同gc cause GC的触发逻辑和影响,为系统开发人员和APP客户端开发人员提供项目GC问题优化思路。 参考资料 1)?Android R源码 2)?《深入理解android java虚拟机art-邓凡平》 3)?老罗的Android之旅-https://blog.csdn.net/Luoshengyang?t=1&type=blog 4)?ART的堆内存布局-https://www.cnblogs.com/YYPapa/p/6851299.html 5)?ART虚拟机 | 如何让GC同步回收native内存-https://juejin.cn/post/6894153239907237902 6)Android GC 简史-https://juejin.cn/post/6966205309782065159 长按关注 内核工匠微信 Linux 内核黑科技 | 技术文章 | 精选教程 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 20:08:04- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |