从API版本26.0.0开始,JSVM-API提供了堆内存管理相关的核心能力,包含获取原始堆快照和监控堆内存阈值两类关键接口:
开发者可通过这些接口实现堆内存的全生命周期监控、快照采集与分析,辅助优化内存使用效率、排查内存相关问题。
JSVM-API提供了OH_JSVM_TakeRawHeapSnapshot接口,来获取VM的原始堆快照。原始堆快照以JSVM专属的二进制格式存储堆内存的完整状态,其格式是与具体VM实现绑定的,数据布局在不同版本之间不保证稳定。获取堆快照的操作可能短暂地暂停应用运行,频繁调用会生成大量快照文件,需开发者合理管控磁盘占用。
此外,快照数据通过自定义的流回调获取时,会在VM运行的线程上同步调用,因此回调函数需避免长时间阻塞操作;若回调返回false,输出流将会被中止,快照生成流程也会立即终止。
JSVM-API提供了OH_JSVM_SetHeapThresholdCallback接口,为指定的VM注册一个堆内存阈值回调函数。一个VM同一时间仅能注册一个堆内存阈值回调,回调通过“阈值(字节数)+回调函数+用户自定义数据”的组合唯一标识,当不再需要该回调时,必须调用OH_JSVM_ClearHeapThresholdCallback进行注销。
该类接口不保证线程安全,必须在VM运行的线程上调用。当堆内存使用量达到指定阈值时,回调函数会被触发,并在同一线程上被同步调用,且回调执行期间会跳过堆阈值检查。
| 接口 | 功能说明 |
|---|---|
| OH_JSVM_TakeRawHeapSnapshot | 获取VM的原始堆快照,并通过流回调输出二进制数据。 |
| OH_JSVM_SetHeapThresholdCallback | 为VM注册堆内存阈值回调,达到阈值时触发自定义逻辑。 |
| OH_JSVM_ClearHeapThresholdCallback | 移除VM中已注册的堆内存阈值回调函数。 |
JSVM-API接口开发流程参考使用JSVM-API实现JS与C/C++语言交互开发流程,本文仅对接口对应C++相关代码进行展示。
包括OH_JSVM_TakeRawHeapSnapshot、OH_JSVM_SetHeapThresholdCallback、OH_JSVM_ClearHeapThresholdCallback三个函数的使用示例,涵盖堆快照采集、堆阈值回调注册/移除、边界场景(重复注册/移除、无效参数)等核心场景。
cpp部分代码
- #include <iostream>
- #include <fstream>
- #include <thread>
- #include <chrono>
- #include <unistd.h>
- #include <sys/types.h>
- #include "napi/native_api.h"
- #include "hilog/log.h"
- #include "ark_runtime/jsvm.h"
-
- #define LOG_DOMAIN 0x3200
- #define LOG_TAG "APP"
-
- static int g_aa = 0;
-
- static bool g_heapThresholdCalled = false;
- static uint64_t g_triggeredThreshold = 0;
- static void* g_callbackUserData = nullptr;
- static bool g_snapshotGenerated = false;
-
- static constexpr int SLEEP_TIME_MS = 100;
- static constexpr uint64_t THRESHOLD_SIZE = 1024 * 1024;
- static constexpr int TEST_DATA_VALUE = 0x12345678;
-
- bool SnapshotStreamCallback(const char* data, int size, void* streamData)
- {
- std::FILE* file = reinterpret_cast<std::FILE*>(streamData);
- if (file) {
- size_t written = std::fwrite(data, 1, size, file);
- return written == static_cast<size_t>(size);
- }
- return true;
- }
-
- void OnHeapThresholdReached(JSVM_VM vm, uint64_t threshold, void* data)
- {
- OH_LOG_INFO(LOG_APP, "== Heap threshold reached ==");
- OH_LOG_INFO(LOG_APP, "Threshold: %{public}lu bytes", threshold);
- OH_LOG_INFO(LOG_APP, "User data: %{public}d", *static_cast<int*>(data));
-
- g_heapThresholdCalled = true;
- g_triggeredThreshold = threshold;
- g_callbackUserData = data;
-
- if (!g_snapshotGenerated) {
- g_snapshotGenerated = true;
- pid_t pid = fork();
- if (pid < 0) {
- OH_LOG_ERROR(LOG_APP, "fork failed");
- } else if (pid == 0) {
- std::FILE* file = std::fopen(
- "/data/storage/el2/base/temp/threshold.rawheap", "wb");
- OH_JSVM_TakeRawHeapSnapshot(vm, SnapshotStreamCallback, file);
- std::fflush(file);
- fclose(file);
- _exit(0);
- }
- }
- }
-
- static void ResetTestState()
- {
- g_heapThresholdCalled = false;
- g_triggeredThreshold = 0;
- g_callbackUserData = nullptr;
- g_snapshotGenerated = false;
- }
-
- static void TestSetHeapThresholdCallback(JSVM_VM vm, uint64_t threshold, int* testData)
- {
- JSVM_Status addStatus = OH_JSVM_SetHeapThresholdCallback(
- vm, threshold, OnHeapThresholdReached, testData);
- if (addStatus == JSVM_OK) {
- OH_LOG_INFO(LOG_APP, "Set heap threshold callback success");
- }
-
- JSVM_Status addRepeatStatus = OH_JSVM_SetHeapThresholdCallback(
- vm, threshold, OnHeapThresholdReached, testData);
- if (addRepeatStatus == JSVM_INVALID_ARG) {
- OH_LOG_INFO(LOG_APP, "Set repeated callback failed (expected)");
- }
-
- JSVM_Status addInvalidStatus = OH_JSVM_SetHeapThresholdCallback(
- vm, 0, OnHeapThresholdReached, testData);
- if (addInvalidStatus == JSVM_INVALID_ARG) {
- OH_LOG_INFO(LOG_APP, "Set callback with 0 threshold failed (expected)");
- }
- }
-
- static void TestClearHeapThresholdCallback(JSVM_VM vm, uint64_t threshold, int* testData)
- {
- JSVM_Status removeStatus = OH_JSVM_ClearHeapThresholdCallback(
- vm, threshold, OnHeapThresholdReached, testData);
- if (removeStatus == JSVM_OK) {
- OH_LOG_INFO(LOG_APP, "Clear heap threshold callback success");
- }
-
- JSVM_Status removeRepeatStatus = OH_JSVM_ClearHeapThresholdCallback(
- vm, threshold, OnHeapThresholdReached, testData);
- if (removeRepeatStatus == JSVM_INVALID_ARG) {
- OH_LOG_INFO(LOG_APP, "Clear repeated callback failed (expected)");
- }
-
- JSVM_Status removeMismatchStatus = OH_JSVM_ClearHeapThresholdCallback(
- vm, 999999, OnHeapThresholdReached, testData);
- if (removeMismatchStatus == JSVM_INVALID_ARG) {
- OH_LOG_INFO(LOG_APP, "Clear mismatch threshold callback failed (expected)");
- }
- }
-
- static void RunAllocScript(JSVM_Env env)
- {
- const char* allocJs = R"JS(
- var holder = [];
- for (let i = 0; i < 10000; i++) {
- holder.push(new Uint8Array(1024));
- }
- )JS";
-
- JSVM_Value jsSrc;
- JSVM_Value result1;
- JSVM_Script script;
- OH_JSVM_CreateStringUtf8(env, allocJs, JSVM_AUTO_LENGTH, &jsSrc);
- OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
- OH_JSVM_RunScript(env, script, &result1);
- }
-
- static bool CheckTestResult(uint64_t threshold, int* testData)
- {
- bool testSuccess = (g_heapThresholdCalled &&
- g_triggeredThreshold == threshold &&
- g_callbackUserData == testData &&
- g_snapshotGenerated);
- if (testSuccess) {
- OH_LOG_INFO(LOG_APP, "Heap management test: SUCCESS");
- } else {
- OH_LOG_ERROR(LOG_APP, "Heap management test: FAILED");
- }
- return testSuccess;
- }
-
- static JSVM_Value HeapMgmtTest(JSVM_Env env, JSVM_CallbackInfo info)
- {
- ResetTestState();
-
- JSVM_VM vm;
- OH_JSVM_GetVM(env, &vm);
- int testData = TEST_DATA_VALUE;
- uint64_t threshold = THRESHOLD_SIZE;
-
- std::FILE* file = std::fopen(
- "/data/storage/el2/base/temp/take.rawheap", "wb");
- JSVM_Status snapshotStatus = OH_JSVM_TakeRawHeapSnapshot(
- vm, SnapshotStreamCallback, file);
- if (snapshotStatus == JSVM_INVALID_ARG) {
- OH_LOG_ERROR(LOG_APP, "Take raw heap snapshot failed (invalid arg)");
- }
-
- TestSetHeapThresholdCallback(vm, threshold, &testData);
- RunAllocScript(env);
-
- OH_JSVM_MemoryPressureNotification(env, JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL);
- std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS));
-
- TestClearHeapThresholdCallback(vm, threshold, &testData);
-
- bool testSuccess = CheckTestResult(threshold, &testData);
-
- JSVM_Value result;
- OH_JSVM_GetBoolean(env, testSuccess, &result);
- return result;
- }
-
- static JSVM_CallbackStruct param[] = {
- {.data = nullptr, .callback = HeapMgmtTest},
- };
-
- static JSVM_CallbackStruct* method = param;
-
- static JSVM_PropertyDescriptor descriptor[] = {
- {"heapMgmtTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
- };
-
- const char* SRC_CALL_NATIVE = R"JS(heapMgmtTest();)JS";
执行结果
在LOG中输出下面结果,同时生成take.rawheap和threshold.rawheap二进制快照文件:
- Set heap threshold callback success
- Set repeated callback failed (expected)
- Set callback with 0 threshold failed (expected)
- == Heap threshold reached ==
- Threshold: 1048576 bytes
- User data: 305419896
- Clear heap threshold callback success
- Clear repeated callback failed (expected)
- Clear mismatch threshold callback failed (expected)
- Heap management test: SUCCESS