鸿蒙系统接入appMemoryOptimizer实现页缓存主动释放
鸿蒙应用在实际运行中存在一笔"看不见的内存账":应用通过 dlopen 加载 so 库、或通过 mmap 读取 HSP 包内资源时,内核会把文件内容映射进页缓存(page cache),这些页会计入进程的内存占用。在 HarmonyOS 7(API 26)之前,应用对此完全无能为力——页缓存归内核管理,既查不到明细,也没有接口主动归还,只能被动等待系统在内存紧张时回收,时机不受控制。HarmonyOS 7(API 26)Release 新增了 @ohos.app.ability.appMemoryOptimizer 模块,把页缓存的主动权交还给应用,这也是本次版本更新中对内存敏感型应用(图片编辑、地图、大型游戏等)最有实用价值的能力之一。
二、技术原理
页缓存的产生机制:当应用加载 so、hap、hsp 等安装产物时,内核为了加速后续访问,会把文件内容缓存到页缓存中。这部分内存出现在进程账单里,但应用既无法枚举也无法释放。
appMemoryOptimizer 提供两个接口(均从 @kit.AbilityKit 导入,仅支持 Stage 模型,SystemCapability 为 SystemCapability.Ability.AbilityRuntime.Core):
evictFilePages(fileNames: Array<string>): Promise<void>——请求系统释放指定文件的页缓存,文件名必须以.so、.hap或.hsp结尾,否则抛出错误码16000163(文件类型错误)。evictModuleFilePages(moduleNames: Array<string>): Promise<void>——按模块名释放页缓存。系统会读取该模块src/main/resources/rawfile/memory_optimizer.json配置文件中的evictFilePages数组,按声明的文件清单逐个释放。这种方式把"哪些文件可以释放"声明化:清单跟着模块走,名单变化不需要改调用代码,重新打包即可;配置解析失败会抛出错误码16000164。
需要注意:释放不保证成功。系统根据当前内存状态决定是否实际执行释放,调用成功(Promise resolve)不代表内存一定下降,真机验证要用 HiDebug 或 Profiler 观察实际水位。
三、实现步骤
-
环境要求:DevEco Studio 26.0.0.821 及以上,
targetSdkVersion/编译 SDK 为 26.0.0(API 26),Stage 模型工程。 -
(可选)在模块的
src/main/resources/rawfile/memory_optimizer.json中声明可释放清单,配合evictModuleFilePages使用:
{
"evictFilePages": [
"libimage_process.so",
"libmap_engine.so"
]
}
- 完整代码文件
src/main/ets/pages/MemoryOptimizerPage.ets,演示两种释放方式、错误处理与onMemoryLevel组合:
import { appMemoryOptimizer, common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'MemoryOptimizerPage';
@Entry
@Component
struct MemoryOptimizerPage {
@State resultMsg: string = '未执行';
private context: common.UIAbilityContext | undefined =
getContext(this) as common.UIAbilityContext;
// 方式一:按文件名释放(只接受 .so / .hap / .hsp 结尾)
private async evictByFileNames(): Promise<void> {
try {
await appMemoryOptimizer.evictFilePages([
'libimage_process.so',
'libmap_engine.so'
]);
this.resultMsg = '按文件名释放请求已提交';
hilog.info(0x0000, TAG, 'evictFilePages ok');
} catch (err) {
// 16000163: 文件类型错误;16000164: 配置文件解析失败
this.resultMsg = `释放失败 code=${err.code} msg=${err.message}`;
hilog.error(0x0000, TAG, 'evictFilePages failed: %{public}d', err.code);
}
}
// 方式二:按模块名释放(清单由 memory_optimizer.json 声明)
private async evictByModuleNames(): Promise<void> {
try {
await appMemoryOptimizer.evictModuleFilePages(['entry']);
this.resultMsg = '按模块释放请求已提交';
} catch (err) {
this.resultMsg = `释放失败 code=${err.code} msg=${err.message}`;
}
}
build() {
Column({ space: 16 }) {
Text('鸿蒙页缓存主动释放示例')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`状态:${this.resultMsg}`)
.fontSize(14)
Button('按文件名释放')
.onClick(() => {
this.evictByFileNames();
})
Button('按模块释放')
.onClick(() => {
this.evictByModuleNames();
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
- 与内存级别监听组合(在 UIAbility 中,退后台或收到低内存信号时执行释放):
import { AbilityConstant, UIAbility, Want, ConfigurationConstant } from '@kit.AbilityKit';
import { appMemoryOptimizer } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 监听系统内存级别
this.context.getApplicationContext().on('memoryLevel', (level) => {
if (level === 1 /* MEMORY_LEVEL_LOW */ || level === 2 /* MEMORY_LEVEL_CRITICAL */) {
appMemoryOptimizer.evictFilePages(['libimage_process.so'])
.then(() => {
console.info('page cache evict requested');
})
.catch((err: BusinessError) => {
console.error(`evict failed: ${err.code}`);
});
}
});
}
}
四、应用场景示例
图片编辑器在用户切走大工程、应用退后台时,把不再使用的图像处理 so 页缓存归还系统;地图应用在用户离线后释放地图引擎 so 的页缓存;游戏在切出对局后释放大体积 HSP 资源缓存。以下是在 Ability 的 onBackground 生命周期中调用的示例:
import { UIAbility } from '@kit.AbilityKit';
import { appMemoryOptimizer } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onBackground(): void {
// 退后台时归还不再使用的 so 页缓存
appMemoryOptimizer.evictFilePages(['libimage_process.so'])
.catch((err: BusinessError) => {
console.error(`evict failed: ${err.code}`);
});
}
}
五、技术要点
- 鉴权与适用范围:该能力为系统开放接口,无需用户授权,但仅支持 Stage 模型,API 26 起可用;FA 模型工程无法使用。
- 时机选择:释放动作建议挂
onBackground或onMemoryLevel()的低内存档位,二者是一对组合拳——监听是"知道该还了",appMemoryOptimizer是"还的动作"。前台高频调用没有意义。 - 错误处理:文件名不以
.so/.hap/.hsp结尾抛16000163,配置文件解析失败抛16000164。把用户任意输入的路径直接塞进evictFilePages没有意义,它只管这三类安装产物,不是通用的文件缓存清理器。 - 结果校验:不要只看 Promise 是否 resolve,真机用 HiDebug 或 Profiler 观察释放前后的进程内存水位,确认收益真实存在。
六、扩展建议
进阶方向一:把可释放清单全部迁移到 evictModuleFilePages 的声明式配置,业务代码只报模块名,so 名单变化不用改代码。进阶方向二:结合支配树分析(DevEco Studio 26.0.0.821 的 ArkTS Snapshot 新增一键获取 Dominator Tree)先定位真正的内存大头,再有针对性地决定哪些 so 需要归还页缓存,避免盲目释放。
结语
本文介绍了 HarmonyOS 7(API 26)新增的 appMemoryOptimizer 页缓存主动释放能力,完整代码不到 100 行,配合 memory_optimizer.json 声明式配置即可接入。对内存敏感的鸿蒙应用来说,这是本次版本更新中投入产出比最高的新能力之一,建议尽快在退后台与低内存场景中落地。
更多推荐
所有评论(0)