鸿蒙 HarmonyOS 7 应用内存优化:appMemoryOptimizer 释放 so 页缓存实战

一、背景说明

随着鸿蒙 HarmonyOS 7(API 26)正式版开启大规模推送,开发者也随之迎来了 API 26 的完整 SDK。HarmonyOS 7.0 Release 在 Ability Kit 中新增了一个非常实用的模块:@ohos.app.ability.appMemoryOptimizer,它提供"主动释放文件页缓存"的能力,让应用可以在退后台等时机把不再使用的 .so、.hap、.hsp 页缓存还给系统,缓解整机内存压力。本文介绍这个模块的原理、完整接入步骤和实战注意事项。

二、技术原理

应用安装后,Native 动态库(.so)和包产物(.hap/.hsp)在被加载执行时,操作系统会将其文件内容以页缓存(page cache)的形式驻留在内存中,即使应用暂时用不到这部分代码,缓存也不会立刻被回收。

appMemoryOptimizer 的思路是"声明式归还":应用主动告诉系统"这些文件的页缓存我暂时不用了",系统在合适的时机将对应页缓存从内存中移除(文件本身在磁盘上,之后再次访问会重新从磁盘读入,正确性不受影响)。该模块提供两个接口:

  • evictFilePages(fileNames: Array<string>): Promise<void>:按文件名清单释放,文件名必须以 .so、.hap 或 .hsp 结尾;
  • evictModuleFilePages(moduleNames: Array<string>): Promise<void>:按模块名释放,系统会读取该模块 src/main/resources/rawfile/memory_optimizer.json 配置文件中的 evictFilePages 数组,按配置清单逐个释放。

第二种方式把"哪些文件可以释放"声明化:文件清单跟着模块走,调用方只报模块名,名单变化时不用改业务代码,重新打包即可。

需要注意两点:其一,释放不保证成功------系统根据当前内存状态决定是否实际执行,Promise resolve 不等于内存已下降,真机验证要用 HiDebug 或 Profiler 看实际水位;其二,仅支持 Stage 模型,SystemCapability 为 SystemCapability.Ability.AbilityRuntime.Core。

三、实现步骤
  1. 环境要求:DevEco Studio 26.0.0.821(Release 版)及以上,compileSdkVersion / targetSdkVersion 为 26.0.0(API 26)。

  2. 在模块 src/main/resources/rawfile/ 下新建 memory_optimizer.json(方式二使用):

json 复制代码
{
  "evictFilePages": [
    "libheavy_model.so",
    "libmedia_codec.so"
  ]
}
  1. 完整代码 entry/src/main/ets/memory/MemoryOptimizer.ets:
typescript 复制代码
import { appMemoryOptimizer } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

export class MemoryOptimizer {
  // 方式一:按文件名清单释放(文件名必须以 .so / .hap / .hsp 结尾)
  static evictByFiles(fileNames: string[]): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      appMemoryOptimizer.evictFilePages(fileNames).then(() => {
        console.info(`evictFilePages ok: ${fileNames.join(',')}`);
        resolve();
      }).catch((err: BusinessError) => {
        // 16000163: 文件类型错误(后缀不合法)
        // 16000164: 配置文件解析失败(方式二)
        console.error(`evictFilePages failed, code=${err.code}, msg=${err.message}`);
        reject(err);
      });
    });
  }

  // 方式二:按模块名释放,清单来自模块 rawfile/memory_optimizer.json
  static evictByModules(moduleNames: string[]): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      appMemoryOptimizer.evictModuleFilePages(moduleNames).then(() => {
        console.info(`evictModuleFilePages ok: ${moduleNames.join(',')}`);
        resolve();
      }).catch((err: BusinessError) => {
        console.error(`evictModuleFilePages failed, code=${err.code}, msg=${err.message}`);
        reject(err);
      });
    });
  }
}
  1. 在 Ability 中配合内存水位回调使用,entry/src/main/ets/entryability/EntryAbility.ets:
typescript 复制代码
import { AbilityConstant, AbilityStage, UIAbility, Want } from '@kit.AbilityKit';
import { MemoryOptimizer } from '../memory/MemoryOptimizer';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 监听系统内存水位,LOW 时归还页缓存
    this.context.getApplicationContext().on('memoryLevel', (level: number) => {
      if (level === 1) { // MEMORY_LEVEL_LOW
        MemoryOptimizer.evictByFiles(['libheavy_model.so']).catch(() => {});
      }
    });
  }

  // 应用退后台是典型时机:把不再使用的 so 页缓存还回去
  onBackground(): void {
    MemoryOptimizer.evictByModules(['entry']).catch(() => {});
  }
}

说明:memoryLevel 回调中 1 对应 MEMORY_LEVEL_LOW(低内存),也可以直接实现 AbilityStage 的 onMemoryLevel。appMemoryOptimizer 不需要额外权限配置,module.json5 无需新增 requestPermissions。

  1. UI 调用示例 entry/src/main/ets/pages/Index.ets:
typescript 复制代码
import { MemoryOptimizer } from '../memory/MemoryOptimizer';

@Entry
@Component
struct Index {
  @State log: string = '点击按钮释放页缓存';

  build() {
    Column({ space: 16 }) {
      Text(this.log).fontSize(16)
      Button('释放指定 so 页缓存')
        .onClick(() => {
          MemoryOptimizer.evictByFiles(['libheavy_model.so'])
            .then(() => this.log = '释放请求已提交')
            .catch(() => this.log = '释放失败,见日志');
        })
    }
    .width('100%').height('100%')
    .justifyContent(FlexAlign.Center)
  }
}
四、应用场景示例

典型场景是端侧 AI 应用:大模型推理引擎的 libllm_runtime.so 动辄几十 MB,推理完一轮对话后短时间不会再执行。可以在推理结束并退后台时归还:

typescript 复制代码
import { common } from '@kit.AbilityKit';

export async function onInferenceDone(context: common.UIAbilityContext): Promise<void> {
  try {
    await MemoryOptimizer.evictByFiles(['libllm_runtime.so', 'libtokenizers.so']);
  } catch (e) {
    // 归还失败不影响业务,下次低内存时系统仍可自行回收
  }
}

另一个场景是含多个功能模块的大型应用(如修图 + 视频 + 直播打包在一个 HAP):用户只用修图时,视频、直播模块的 so 页缓存都可以通过 evictModuleFilePages(['video_module']) 一次性声明归还。

五、技术要点
  1. 不要把 resolve 当成"内存已降" :官方文档明确释放不保证成功,验证请用 HiDebug getPss 或 DevEco Profiler 观察实际水位。
  2. 时机选择 :最佳挂点是 onBackground / onMemoryLevel(MEMORY_LEVEL_LOW);前台高频调用没有意义,还可能触发二次读盘开销。
  3. 错误处理 :文件名后缀不合法抛 16000163,配置文件解析失败抛 16000164。把用户任意输入的路径塞进 evictFilePages 没有意义------它只管安装产物,不是通用文件缓存清理器。
  4. 性能注意:释放后若代码再次执行,页面会从磁盘重新读入,存在一次缺页开销。对延迟敏感的路径(正在播放的解码库、正在渲染的引擎)不要归还。
  5. 版本限制 :仅 Stage 模型、API 26 及以上可用;低版本上调用会报接口不存在,需做 canIUse 或 try/catch 兜底。
六、扩展建议

可以进一步封装一个"水位分级归还"策略:MEMORY_LEVEL moderate 只归还低频模块,low 归还全部可释放清单。用 Promise.all 批量提交:

typescript 复制代码
Promise.all([
  MemoryOptimizer.evictByModules(['video_module']),
  MemoryOptimizer.evictByFiles(['libheavy_model.so'])
]).catch(() => {});

在分布式应用中,还可以结合设备负载信息,让算力富裕的设备不做归还、内存紧张的设备积极归还,形成跨设备差异化的内存策略。

结语

appMemoryOptimizer 是 HarmonyOS 7(API 26)带给开发者的一个"小而实"的接口:一个配置文件加几十行代码,就能把空闲 so 的页缓存主动还给系统。配合 onMemoryLevel 监听使用,对大体积 Native 库的应用(AI、音视频、游戏引擎)是实打实的内存收益,建议在适配 API 26 时顺手接入。

相关推荐
ZZHow10242 小时前
神经网络与深度学习笔记_Day1
人工智能·pytorch·笔记·python·深度学习·神经网络·机器学习
在所不辞兄2 小时前
【人工智能驱动的电催化研究-01】人工智能是什么?AI、机器学习、深度学习和神经网络之间有什么区别?
人工智能·深度学习·神经网络·机器学习·有限元仿真·电化学·电催化
熊猫钓鱼>_>2 小时前
三方库 NDK 原生模块跨平台适配与调试【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙心迹
A7bert7772 小时前
【MobileSAM部署至RK3588】环境适配→转换RKNN→开发板部署
c++·人工智能·深度学习·目标检测·ubuntu
如若1234 小时前
TRACER 论文解读与复现:面向 ICU 长时程患者监测的多模态 Harness 架构、时序证据建模与工具增强推理
人工智能·深度学习·计算机视觉
行者-全栈开发13 小时前
【鸿蒙心迹】三方库移植(二)——CMake 交叉编译与符号冲突排查实录(HarmonyOS 7.x)
harmonyos·cmake·交叉编译·三方库移植·ohos·符号冲突
梦想不只是梦与想15 小时前
鸿蒙三方库源码调试
harmonyos·断点调试·三方源码调试
用户1179104883317 小时前
AI输出满屏井号星号?hmharness把Markdown渲染成终端表格 GitHub:swsgbl/hmharness
ai编程·harmonyos
了一12319 小时前
鸿蒙ArkWeb 长截图(滚动截图)技术方案总结
harmonyos