Systemui Java堆 GC 差异分析
来源:飞书 wiki(obj_token: S8IedyqVQoUFhAxufHxctzpNnUc)
👀 从仅抓showmap的结果看: P1 daily版本有一次java堆大幅降低近180M,最后稳定在49-50M左右 vivo有一次java堆大幅降低80M,最后稳定在38M P1 堆总量峰值偏高220M,下降幅度大;Vivo 堆总量偏低116M,下降幅度小
📷 图片描述: 图片展示的是P1 daily - iter2 - 谱名 addCriterion()(token: QvESbmoyIoQ98DxKLj3cLGq7n7b)
📷 图片描述: 图片展示的是vivo8750 - iter2 - 匿名页 - Java的RSS Change Map。横轴为Round,从0到41;纵轴为匿名页Java RSS Value (KB),范围在0到120000。图中蓝色折线呈现数据变化,初始值为114592KB,随后在第1轮降至112500KB,之后在第2轮大幅下降至3500KB,之后基本稳定在3000 - 4000KB左右,最后在第41轮降至2714KB。该图与文档中分析系统UI Java堆GC差异的内容相关,用于直观呈现匿名页Java RSS值的变化情况。(token: Qginb9fNTo42WsxqacGcs51bnff)
查看log分析
P1 GC log:
12-18 23:50:17.598 19534 19552 I ndroid.systemui: Background concurrent copying GC freed 119MB AllocSpace bytes, 74(6948KB) LOS objects, 72% free, 36MB/132MB, paused 39us,19us total 102.137ms
12-18 23:50:32.879 19534 19558 I ndroid.systemui: Explicit concurrent copying GC freed 4904KB AllocSpace bytes, 2(44KB) LOS objects, 73% free, 34MB/130MB, paused 70us,50us total 124.130ms
Vivo GC log
01-08 21:40:05.440 29872 29881 I ndroid.systemui: NativeAlloc concurrent copying GC freed 49MB AllocSpace bytes, 54(1072KB) LOS objects, 49% free, 17MB(NonMov:584KB, MS:15MB + LOS:1800KB)/34MB, paused 72us,36us total 127.658ms cpu 119.789ms, itvl 1021.977s, na, bg
01-08 21:40:05.837 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 15MB AllocSpace bytes, 6(96KB) LOS objects, 19% free, 28MB(NonMov:584KB, MS:24MB + LOS:2704KB)/34MB, paused 58us,38us total 37.249ms cpu 36.326ms, itvl 486.921ms, na, bg
01-08 21:40:05.959 29872 29881 I ndroid.systemui: Background concurrent copying GC freed 21MB AllocSpace bytes, 1(16KB) LOS objects, 43% free, 31MB(NonMov:584KB, MS:27MB + LOS:2688KB)/55MB, paused 77us,41us total 99.454ms cpu 97.840ms, itvl 60.191ms, na, bg
01-08 21:40:06.107 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 34MB AllocSpace bytes, 3(140KB) LOS objects, 45% free, 28MB(NonMov:584KB, MS:25MB + LOS:2688KB)/52MB, paused 107us,37us total 35.269ms cpu 34.557ms, itvl 211.696ms, na, bg
01-08 21:40:06.232 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 31MB AllocSpace bytes, 1(16KB) LOS objects, 47% free, 26MB(NonMov:584KB, MS:23MB + LOS:2688KB)/50MB, paused 47us,35us total 29.259ms cpu 28.811ms, itvl 131.124ms, na, bg
01-08 21:40:06.365 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 28MB AllocSpace bytes, 0(0B) LOS objects, 47% free, 26MB(NonMov:584KB, MS:23MB + LOS:2688KB)/50MB, paused 102us,37us total 29.413ms cpu 28.791ms, itvl 132.715ms, na, bg
01-08 21:40:06.501 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 28MB AllocSpace bytes, 0(0B) LOS objects, 46% free, 26MB(NonMov:584KB, MS:23MB + LOS:2688KB)/50MB, paused 53us,34us total 29.175ms cpu 27.075ms, itvl 136.316ms, na, bg
01-08 21:40:06.637 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 28MB AllocSpace bytes, 1(16KB) LOS objects, 46% free, 26MB(NonMov:584KB, MS:23MB + LOS:2688KB)/50MB, paused 88us,35us total 28.291ms cpu 27.299ms, itvl 137.278ms, na, bg
01-08 21:40:06.774 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 27MB AllocSpace bytes, 4(112KB) LOS objects, 41% free, 29MB(NonMov:584KB, MS:21MB + LOS:7392KB)/50MB, paused 102us,41us total 29.229ms cpu 28.713ms, itvl 135.898ms, na, bg
01-08 21:40:06.822 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 2437KB AllocSpace bytes, 25(16MB) LOS objects, 0% free, 64MB(NonMov:584KB, MS:21MB + LOS:42MB)/64MB, paused 47us,35us total 27.084ms cpu 23.625ms, itvl 50.235ms, na, bg
01-08 21:40:06.908 29872 29881 I ndroid.systemui: Background concurrent copying GC freed 8954KB AllocSpace bytes, 34(54MB) LOS objects, 18% free, 102MB(NonMov:584KB, MS:26MB + LOS:75MB)/126MB, paused 50us,70us total 86.020ms cpu 83.827ms, itvl 27.341ms, na, bg
01-08 21:40:06.952 29872 29881 I ndroid.systemui: Background young concurrent copying GC freed 12MB AllocSpace bytes, 14(32MB) LOS objects, 19% free, 98MB(NonMov:584KB, MS:23MB + LOS:74MB)/122MB, paused 87us,63us total 33.132ms cpu 28.027ms, itvl 96.692ms, na, bg
01-08 21:40:07.312 29872 29881 I ndroid.systemui: Background concurrent copying GC freed 12MB AllocSpace bytes, 197(110MB) LOS objects, 49% free, 19MB(NonMov:584KB, MS:16MB + LOS:1908KB)/38MB, paused 53us,35us total 86.825ms cpu 85.950ms, itvl 305.862ms, na, bg
01-08 21:40:24.955 29872 29944 I ndroid.systemui: Explicit concurrent copying GC freed 1671KB AllocSpace bytes, 1(20KB) LOS objects, 50% free, 18MB(NonMov:584KB, MS:16MB + LOS:1928KB)/36MB, paused 66us,37us total 82.102ms cpu 80.896ms, itvl 17.647s, na, bg
p1和vivo的gc配置参数差异
👀 P1: [dalvik.vm.heapgrowthlimit]: [256m] [dalvik.vm.heapmaxfree]: [32m] [dalvik.vm.heapminfree]: [8m] [dalvik.vm.heapsize]: [512m] [dalvik.vm.heapstartsize]: [16m] [dalvik.vm.heaptargetutilization]: [0.5] vivo: [dalvik.vm.heapgrowthlimit]: [256m] [dalvik.vm.heapmaxfree]: [8m] [dalvik.vm.heapminfree]: [2m] [dalvik.vm.heapsize]: [512m] [dalvik.vm.heapstartsize]: [8m] [dalvik.vm.heaptargetutilization]: [0.75]
差异:
[dalvik.vm.heapmaxfree]
P1:GC 后空闲内存超过 32M 才会触发堆收缩,容忍更多的空闲内存留存;
Vivo:GC 后空闲内存超过 8M 就会触发堆收缩,对空闲内存的容忍度极低,收缩更激进;
差异影响:Vivo 会更频繁、更早地触发堆收缩,释放冗余内存,避免堆总量偏高
[dalvik.vm.heapminfree]
P1:空闲内存低于 8M 才会扩容堆,扩容阈值更高,预留更多空闲内存;
Vivo:空闲内存低于 2M 才会扩容堆,扩容阈值更低,仅在必要时扩容;
差异影响:Vivo 堆扩容更谨慎,避免堆总量无意义增长
[dalvik.vm.heaptargetutilization]
P1:期望堆内存使用比例为 50%,即总堆容量的一半用于存储对象,一半保留为空闲内存;
Vivo:期望堆内存使用比例为 75%,即总堆容量的 75% 用于存储对象,仅 25% 保留为空闲内存;
差异影响:相同使用内存下,Vivo 所需的总堆容量更小
👀 gc频次差异原因: Vivo 的目标利用率 75%,堆内存利用更紧凑,空闲内存余量少(仅 25%); 当进程有少量对象分配时,空闲内存容易快速低于 2M 的最小空闲阈值,触发小型 GC(Young GC)回收垃圾,补充空闲内存; 这种高频小型 GC 是 Vivo紧凑利用内存的代价,换来的是堆总量的大幅降低,为优先控制内存占用的策略。 showmap峰值、降幅差异: 上述参数差异,造成形成P1 堆总量偏高,下降幅度大;Vivo 堆总量偏低,下降幅度小: p1: heaptargetutilization=0.5(低利用率)→ heapmaxfree=32M(高空闲阈值)→ 堆扩容宽松,收缩保守 → 前期堆总量偏高(120-130M),积累大量空闲内存 → 最终满足批量收缩条件,释放近 180M 内存 → showmap稳定在 49-50M。 vivo: heaptargetutilization=0.75(高利用率)→ heapmaxfree=8M(低空闲阈值)→ 堆扩容谨慎,收缩激进 → 前期堆总量偏低(34-50M),无大量空闲内存积累 → 最终仅释放 80M 内存 → showmap稳定在 38M 左右。
👀 经和ART模块讨论,最终结论: 1.测试过程中,虚拟机参数只会影响gc的频次,不会影响多次gc回收的总量: P1回收了100M+,vivo回收了500M左右,P1堆量增长明显优于对比机,无需分析 java堆分配过程的总量; 若其他case发现gc回收量P1更多,可以结合火焰图分析 2.工具主动GC(dumpsys meminfo)之后,P1 44M/vivo 27M,相差17M左右,和log中显示的主动gc之后 p1 34MB/vivo 18M,差值基本相符,这部分是真正业务逻辑造成的java存活对象的差异,和虚拟机参数无关,需要重点关注,最好根据heapdump(hprof)分析 3.P1 gc参数和策略不同,造成gc堆峰值和频次的差异,P1systemui的heap峰值比较高的原因是对systemui有帧感知的GC抑制
帧感知gc:
📷 图片描述: 图片展示了Android系统中Choreographer.java文件的代码片段,重点标注了两处mSmartArtManager的doFrameStart()和doFrameEnd()方法调用。该图片与文档中关于帧感知GC的优化手段相关,说明了系统ui有帧感知的GC抑制,通过这些代码片段可看出系统在帧渲染过程中对GC的控制,与文档中对帧感知GC的解释相呼应。(token: X8Zrb7IJhorkDlxCjMnc7qZZnJf)
📷 图片描述: 图片展示的是Android系统中systemui的火焰图。上方为系统时间线,显示了系统各模块的运行情况。下方是火焰图,以不同颜色标识了不同类别的垃圾回收(GC)事件,如Full GC、Concurrent GC等,还标注了Heap使用量(MB)。火焰图中红色突出显示了Heap使用量较高的部分,与上文提到的systemui对帧感知的GC抑制相关,可据此分析heap峰值高的原因。(token: VykobW2NqoofjbxxbGGcKKdlncf)
优化手段:
- https://gerrit.pt.mioffice.cn/c/platform/art/+/5976085 java heap内存差距可以带上这笔patch试下,W上gc之后这部分内存不会直接释放给系统