一、背景说明

HarmonyOS 7.0(API 26.0.0)Release 新增了一个对性能敏感的应用很实用的模块:@ohos.app.ability.appMemoryOptimizer。它解决的是一个长期存在的痛点:应用通过 dlopen 加载 .so、或 mmap 读取 .hsp 资源时,内核会把文件内容映射进页缓存(file page cache),这些页计入进程内存占用,但应用既看不到明细,也没有接口主动归还——系统只会在内存紧张时自行回收,时机完全不受控。例如图片编辑器切换大工程后、地图应用退后台后,明明不再使用某些大 so,RSS 却降不下来。这个新模块把"归还页缓存"的主动权交还给了应用。

二、技术原理

页缓存是内核为加速文件读写而维护的内存映射层。so/HSP 这类安装产物被加载后,即使逻辑上已卸载,其文件页仍可能驻留在缓存中,表现为进程内存居高不下(用 HiDebug 排查时,大头往往既不是 ArkTS 堆也不是 Native 堆,而是这部分"暗账")。

appMemoryOptimizer 提供两个接口,均从 @kit.AbilityKit 导入:

接口作用入参约束
evictFilePages(fileNames: Array<string>)释放指定文件的页缓存文件名必须以 .so、.hap、.hsp 结尾
evictModuleFilePages(moduleNames: Array<string>)按模块释放页缓存模块需在 rawfile/memory_optimizer.json 中声明文件清单

调用链是:业务层传入文件名或模块名 → 系统根据当前内存水位判断是否真正执行释放 → 结果以 Promise 返回。第二种方式把"哪些文件可以释放"声明化:文件清单跟着模块打包,名单变化不需要改调用代码。

三、实现步骤

环境要求:DevEco Studio 26.0.0.821 及以上,compileSdkVersion 26.0.0,仅支持 Stage 模型(SystemCapability 为 SystemCapability.Ability.AbilityRuntime.Core)。该接口不涉及敏感行为,module.json5 无需额外权限声明。

步骤 1:新建工程并引入模块

// entry/src/main/ets/pages/Index.ets
import { appMemoryOptimizer } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

步骤 2:方式一——直接指定文件释放

// 请求释放指定 so / hsp 的页缓存
try {
  appMemoryOptimizer.evictFilePages(['libentry.so', 'sharedLibrary.hsp'])
    .then(() => {
      console.info('EvictFilePages success.');
    })
    .catch((err: BusinessError) => {
      console.error(`EvictFilePages failed, code: ${err.code}, msg: ${err.message}.`);
    });
} catch (error) {
  console.error('EvictFilePages failed: ' + JSON.stringify(error));
}

步骤 3:方式二——按模块声明式释放

先在 entry/src/main/resources/rawfile/memory_optimizer.json 中声明可释放的文件清单:

{
  "evictFilePages": [
    "libentry.so",
    "sharedLibrary.hsp"
  ]
}

之后业务代码只需报模块名,系统会读取该配置逐个释放:

try {
  appMemoryOptimizer.evictModuleFilePages(['entry'])
    .then(() => console.info('EvictModuleFilePages success.'))
    .catch((err: BusinessError) => {
      console.error(`failed, code: ${err.code}, msg: ${err.message}.`);
    });
} catch (error) {
  console.error('EvictModuleFilePages failed: ' + JSON.stringify(error));
}

步骤 4:在 UI 中调用

结合设备内存水位回调,在应用退后台时主动归还:

import { appMemoryOptimizer } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

async function releaseMemory(): Promise<void> {
  try {
    await appMemoryOptimizer.evictFilePages(['libentry.so']);
    console.info('page cache evicted.');
  } catch (err) {
    const e = err as BusinessError;
    console.error(`evict failed, code: ${e.code}, msg: ${e.message}.`);
  }
}

@Entry
@Component
struct Index {
  build() {
    Column({ space: 12 }) {
      Text('appMemoryOptimizer 示例')
        .fontSize(20)
      Button('释放页缓存')
        .onClick(() => {
          releaseMemory();
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}
四、应用场景示例

更贴近实战的做法是在 Ability 生命周期中挂接。进入后台时释放不再使用的 so 页缓存:

// entry/src/main/ets/entryability/EntryAbility.ets
import { appMemoryOptimizer } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

export default class EntryAbility extends UIAbility {
  onBackground(): void {
    appMemoryOptimizer.evictFilePages(['libheavy.so'])
      .then(() => console.info('background evict ok.'))
      .catch((err: BusinessError) =>
        console.error(`evict failed: ${err.code}, ${err.message}`));
  }
}

也可以配合 onMemoryLevel():当收到 MEMORY_LEVEL_LOW 时执行模块级释放,形成"监听是知道该还了、appMemoryOptimizer 是还的动作"的组合拳。

五、技术要点
  1. 释放不保证成功。系统根据当前内存状态决定是否实际执行,Promise resolve 不等于内存已下降。真机验证请用 HiDebug 或 Profiler 观察实际水位,不要只看调用是否成功。
  2. 错误码:文件名不以 .so/.hap/.hsp 结尾抛 16000163(文件类型错误);配置文件解析失败抛 16000164。建议统一 try/catch 并按 code 区分处理。
  3. 调用时机:典型时机是 onBackground、onMemoryLevel(MEMORY_LEVEL_LOW),避免在页面切换的高频路径里反复调用;页缓存本身服务文件读加速,频繁释放反而可能增加下次加载开销。
  4. 边界:它只管理安装产物(.so/.hap/.hsp)的页缓存,不是通用的文件缓存清理器,把用户输入的任意路径塞进去没有意义。
  5. 性能建议:释放动作本身是异步 Promise,可放在 worker 之外直接 await;若一次释放大量文件,建议分批处理并记录日志便于观测收益。
六、扩展建议
  • 结合支配树工具链:DevEco Studio 26.0.0.821 的 ArkTS Snapshot 新增了支配树(Dominator Tree)分析,可与本接口配合——先用支配树定位泄漏/高占用,再用 evictFilePages 归还可释放的文件页,构成完整的内存治理闭环。
  • 构建期去重:build-profile.json5 的 packOptions 新增 deduplicateSo 字段,可去除 HAP/HSP 中的重复 so,从源头减少页缓存占用:
{
  "buildOption": {
    "packOptions": {
      "deduplicateSo": true
    }
  }
}
结语

appMemoryOptimizer 只有两个接口、不到二十行调用代码,却补上了鸿蒙内存管理里"应用无法主动归还页缓存"这一环。配合 onMemoryLevel 生命周期与 HiDebug 观测,即可在高内存场景下实现可控的内存回收,实用性很高,建议 API 26 项目直接接入。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐