从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中已注册的堆内存阈值回调函数。 |
包括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_ERROR(LOG_APP, "Set repeated callback failed (expected)");
}
JSVM_Status addInvalidStatus = OH_JSVM_SetHeapThresholdCallback(
vm, 0, OnHeapThresholdReached, testData);
if (addInvalidStatus == JSVM_INVALID_ARG) {
OH_LOG_ERROR(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_ERROR(LOG_APP, "Clear repeated callback failed (expected)");
}
JSVM_Status removeMismatchStatus = OH_JSVM_ClearHeapThresholdCallback(
vm, 999999, OnHeapThresholdReached, testData);
if (removeMismatchStatus == JSVM_INVALID_ARG) {
OH_LOG_ERROR(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