ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【鸿蒙(HarmonyOS)性能优化指南】内存分析器Allocation Profiler [打印本页]

作者: 王國慶    时间: 昨天 04:51
标题: 【鸿蒙(HarmonyOS)性能优化指南】内存分析器Allocation Profiler
DevEco Studio集成的DevEco Profiler性能调优工具(以下简称为Profiler),提供Time、Allocation、Snapshot、CPU等场景化分析任务类型。开辟者可使用Profiler的Allocation内存分析器,在应用或服务运行时实时显示内存使用情况,辨认可能会导致应用卡顿、内存走漏、内存抖动的标题,或找到导致内存瓶颈的标题。
使用束缚

已通过USB毗连设备并在设备上打开必要调试的设备。
仅在应用为debug编译模式时使用
仅支持OpenHarmony API 9及以上版本的Stage工程
场景示例

本示例设置两个页面,通过内存分析器来分析页面跳转场景下是否存在内存分配不合理的标题。
主页面代码如下:
  1. import router from '@ohos.router'
  2. @Entry
  3. @Component
  4. struct Index {
  5. build() {
  6.   Row() {
  7.    Column() {
  8.     Button("点击跳转").onClick(() => {
  9.      onJumpClick();
  10.     })
  11.    }
  12.    .width('100%')
  13.   }
  14.   .height('100%')
  15. }
  16. }
  17. // 跳转下个页面
  18. function onJumpClick(): void {
  19. router.pushUrl({
  20.   url: 'pages/second' // 目标url
  21. }, router.RouterMode.Standard, (err) => {
  22.   if (err) {
  23.    console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
  24.    return;
  25.   }
  26.   console.info('Invoke pushUrl succeeded.');
  27. });
  28. }
复制代码
跳转后页面代码如下:
  1. @Component
  2. struct SwiperChild {
  3. @State arr: number[] = [];
  4. aboutToAppear(): void {
  5. for (let i = 1; i <= 10; i++) {
  6.   this.data.push(i);
  7. }
  8. }
  9. build() {
  10.   Column() {
  11.    List({ space: 20 }) {
  12.     ForEach(this.arr, (index: number) => {
  13.      ListItem() {
  14.       Text(index.toString())
  15.        .height('4.5%')
  16.        .fontSize(16)
  17.        .textAlign(TextAlign.Center)
  18.        .backgroundColor(0xFFFFFF)
  19.      }
  20.      .border({ width: 2, color: Color.Green })
  21.     }, (index: number) => index.toString());
  22.    }
  23.    .height("95%")
  24.    .width("95%")
  25.    .border({ width: 3, color: Color.Red })
  26.    .lanes({ minLength: 40, maxLength: 40 })
  27.    .alignListItem(ListItemAlign.Start)
  28.    .scrollBar(BarState.Off)
  29.   }.width('100%').height('100%').padding({ top: 5 });
  30. }
  31. }
  32. @Entry
  33. @Preview
  34. @Component
  35. struct SecondPage {
  36. @State data: Number[] = [];
  37. aboutToAppear(): void {
  38.   let list = []
  39.   for (let i = 1; i <= 100; i++) {
  40.    list.push(i);
  41.   }
  42. }
  43. build() {
  44.   Column({ space: 5 }) {
  45.    Swiper() {
  46.     ForEach(this.data, (_: number) => {
  47.      ChildPage();
  48.     }, (item: number) => item.toString());
  49.    }
  50.    .loop(false)
  51.    .indicator(true)
  52.    .duration(100)
  53.    .curve(Curve.Linear)
  54.   }.width('100%')
  55.   .margin({ top: 5 })
  56. }
  57. }
复制代码
如何创建内存分析任务

以DevEco Studio 4.0.0.400版本为例,在DevEco Studio菜单栏上单击****View**** > *Tool Windows* > *Profiler*,大概在DevEco Studio底部工具栏单击Profiler按钮,打开Profiler性能分析器,点选Allocation选项,再点击Create Session按钮创建耗时分析任务。

点击 按钮以开关Launch Mode,开启后,任务开始时应用会自动冷启动,有助于分析启动场景下的应用内存标题。
内存分析任务支持在录制前单击 按钮指定要录制的泳道:

Memory泳道:按照所选维度,显示框选时间段的内存统计数据。在该泳道的下拉列表中,可选择显示维度:
选择“Usage”,显示指定占用类型的内存数据,当前支持的内存占用类型包括:
PSS:进程独占内存和按比例分配共享库占用内存之和。
RSS:进程独占内存和相关共享库占用内存之和。
USS:进程独占内存。
选择“Summary”,显示指定应用类型的内存数据,当前支持的内存类型包括:
ArkTS:方舟虚拟机占用的内存。
Native:C或C++代码所占用的内存。
Stack:应用/服务中栈上使用的内存。
Code:应用/服务用于处理惩罚代码和资源的内存。
Others:其他不归属以上分类的内存。
单击上述内存类型标识,可在Memory的泳道堆叠图中隐藏此类内存。方便开辟者查看单类内存的使用趋势。
ArkTS Allocation泳道:显示方舟虚拟机上的内存分配信息。
Native Allocation泳道:显示具体的Native内存分配情况,包括静态统计数据、分配栈、每层函数栈消耗的Native内存等信息。单击Native Allocation泳道的“options”下拉列表,可以设置最小跟踪内存和栈深度。默认最小跟踪内存为4096Bytes,默认栈深度为10层。设置完成后,在录制期间小于此大小的内存分配将被忽略,最大栈深度将到达设置的值。

如果你对这篇鸿蒙开辟的技术分析感兴趣,还请帮忙来个“素质三连”,后续带你探索 “→更多←(0区下方籽料)” 鸿蒙开辟的技术奥秘!


提示

设置的最小跟踪内存数值越小、栈深度越大,对应用造成的影响就越大,可能会导致Profiler卡顿。请根据应用实际的调测情况进行合理设置。
在任务分析窗口,可以通过“Ctrl+鼠标滚轮”缩放时间轴,通过“Shift+鼠标滚轮”左右移动时间轴。
内存分析任务会降低设备的性能,请勿将分析结果作为性能参考。
内存分析任务中的使用


点击“Collect garbage”按钮可启动内存接纳机制。当方舟虚拟机的调优对象的某个步伐/进程占用的部门内存空间在后续的使用中不再被该对象访问时,内存接纳机制会自动将这部门空间归还给系统,降低步伐错误概率,减少不必要的内存损耗。通常在分析启动时触发,用于降低内存接纳对内存统计正确性的影响。
点击 按钮制止内存分析任务。
针对示例场景,在内存分析任务开始后,进行跳转页面动作。
留意

在任务录制(recording)及分析(analyzing)的过程中,请不要自动断开应用大概设备,否则可能导致分析任务非常失败。
进行内存分析

在目标泳道上长按鼠标左键并拖拽,框选要展示分析的时间段。
Details地区中显示此时间段内指定类型的内存分析统计信息:
Memory泳道:显示指定维度的内存统计信息。
Usage维度:显示当前框选时间段内PSS内存统计信息。该维度下,默认只显示PSS的统计图,如必要查看USS或RSS,必要在Memory泳道的右上角点选相关数据类型。可以看出,在页面跳转开始1.6s内,PSS内存急剧增加。

Summary维度:显示当前框选时间段内全部类型的应用内存的峰谷值与平均值统计信息。可以看出,在页面跳转开始1.6s内,应用内存占用高低差值到达了35MB。

ArkTS Allocation泳道:显示当前框选时段内运行对象的内存使用情况,包括层级、对象自身内存大小、对象关联内存大小等。

“Details”地区中带 标识的对象,表示其可以通过窗口访问。
Native Allocation泳道:框选子泳道后显示具体的内存分配,包括静态统计数据、分配栈等。

Statistics页签中显示该段时间内的静态分配情况,包括分配方式(Malloc或Mmap)、总分配内存大小、总分配次数、尚未释放的内存大小、尚未释放次数、已释放的内存大小、已释放次数。双击任意对象,可跳转至此类对象的详细占用/分配信息。
Call Info页签显示线程的调用栈的内存分配情况,包括对象名称、占用空间、类型等。单击任一调用栈,“More”地区将显示内存分配最多的调用栈。
Allocations List显示内存分配的详细信息,包括内存块起始地址、时间戳、当前活动状态、大小、调用的库、调用库的具体函数、变乱类型(与Statistics页签的分配方式对应)等。选择任一对象,右侧会展示与该对象相关的全部库和调用者。
根据分析结果,双击可能存在标题的调用栈,如调用栈涉及应用代码,则跳转至相关代码。开辟者可根据实际必要进行优化。
分析数据筛选

内存分析器提供多种数据筛选方式,方便开辟者缩小分析范围,更准确地定位标题所在。
通过内存状态筛选


在内存分析过程中,对“Native Allocation”泳道的内存状态信息进行过滤,便于开辟者定位内存标题。在“Native Allocation”泳道的“Detail”地区左下方的下拉框中,可以选择过滤内存状态:
All:详情地区展示当前框选时间段内的全部内存分配信息。
Existing:详情地区展示当前框选时间段内分配未释放的内存。
Released:详情地区展示当前框选时间段内分配已释放的内存。
通过统计方式筛选


在“Native Allocation”泳道的“Statistics”页签中,可以打开“Native Size”选择统计方式以过滤统计数据:
Native Size:详情地区按照对象的原生内存进行展示。
Native Library:详情地区按照对象的so库进行展示。
通过so库名筛选


在“Native Allocation”泳道的“Allocations List”页签中,可以单击“Click to choose”选择要筛选的so库以过滤出与目标so库相关的数据,资助应用发现内存占用过大的动态库。
如果你对这篇鸿蒙开辟的技术分析感兴趣,还请帮忙来个“素质三连”,后续带你探索 “→更多←(0区下方籽料)” 鸿蒙开辟的技术奥秘!


分析结论

通过以上分析得出结论:应用在进行页面跳转时内存占用过大,必要优化。可将跳转后页面中的Swiper组件搭LazyForEach进行懒加载使用以优化内存占用。
代码优化

Swiper组件使用LazyForEach懒加载机制更换ForEach,使得页面跳转时不一次性加载全部的Swiper子组件。跳转后页面代码修改为如下所示:
  1. class MyDataSource implements IDataSource { // LazyForEach的数据源
  2. private list: number[] = [];
  3. constructor(list: number[]) {
  4.   this.list = list;
  5. }
  6. totalCount(): number {
  7.   return this.list.length;
  8. }
  9. getData(index: number): number {
  10.   return this.list[index];
  11. }
  12. registerDataChangeListener(_: DataChangeListener): void {
  13. }
  14. unregisterDataChangeListener(): void {
  15. }
  16. }
  17. @Component
  18. struct ChildPage { // 带有100个ListItem的List组件
  19. @State arr: number[] = [];
  20. aboutToAppear(): void {
  21.   for (let i = 1; i <= 100; i++) {
  22.    this.arr.push(i);
  23.   }
  24. }
  25. build() {
  26.   Column() {
  27.    List({ space: 20 }) {
  28.     ForEach(this.arr, (index: number) => {
  29.      ListItem() {
  30.       Text(index.toString())
  31.        .height('4.5%')
  32.        .fontSize(16)
  33.        .textAlign(TextAlign.Center)
  34.        .backgroundColor(0xFFFFFF)
  35.      }
  36.      .border({ width: 2, color: Color.Green })
  37.     }, (index: number) => index.toString());
  38.    }
  39.    .height("95%")
  40.    .width("95%")
  41.    .border({ width: 3, color: Color.Red })
  42.    .lanes({ minLength: 40, maxLength: 40 })
  43.    .alignListItem(ListItemAlign.Start)
  44.    .scrollBar(BarState.Off)
  45.   }.width('100%').height('100%').padding({ top: 5 });
  46. }
  47. }
  48. @Entry
  49. @Preview
  50. @Component
  51. struct SecondPage {
  52. private dataSrc: MyDataSource = new MyDataSource([]);
  53. aboutToAppear(): void {
  54.   let list = []
  55.   for (let i = 1; i <= 100; i++) {
  56.    list.push(i);
  57.   }
  58.   this.dataSrc = new MyDataSource(list);
  59. }
  60. build() {
  61.   Column({ space: 5 }) {
  62.    Swiper() {
  63.     LazyForEach(this.dataSrc, (_: number) => { // 使用懒加载机制
  64.      ChildPage();
  65.     }, (item: number) => item.toString());
  66.    }
  67.    .loop(false)
  68.    .cachedCount(1) // 提前加载后一项的内容
  69.    .indicator(true)
  70.    .duration(100)
  71.    .curve(Curve.Linear)
  72.   }.width('100%')
  73.   .margin({ top: 5 })
  74. }
  75. }
复制代码
优化结果

如图所示,优化后,页面跳转时应用内存占用高低差值从35MB降低到了7MB。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4