鸿蒙操作系统使用appMemoryOptimizer主动释放页缓存内存
一、背景说明
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 是还的动作"的组合拳。
五、技术要点
- 释放不保证成功。系统根据当前内存状态决定是否实际执行,Promise resolve 不等于内存已下降。真机验证请用 HiDebug 或 Profiler 观察实际水位,不要只看调用是否成功。
- 错误码:文件名不以
.so/.hap/.hsp结尾抛16000163(文件类型错误);配置文件解析失败抛16000164。建议统一 try/catch 并按 code 区分处理。 - 调用时机:典型时机是
onBackground、onMemoryLevel(MEMORY_LEVEL_LOW),避免在页面切换的高频路径里反复调用;页缓存本身服务文件读加速,频繁释放反而可能增加下次加载开销。 - 边界:它只管理安装产物(.so/.hap/.hsp)的页缓存,不是通用的文件缓存清理器,把用户输入的任意路径塞进去没有意义。
- 性能建议:释放动作本身是异步 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 项目直接接入。
更多推荐



所有评论(0)