文档管理中心
我的
您当前正在浏览新版开发者文档中心,目录分类和层级有所调整。点击左侧当前文档分类名称前的“☰”图标,可切换文档分类。 了解新版目录
指南与API参考指南NDK开发代码开发使用JSVM-API实现JS与C/C++语言交互JSVM-API使用指导使用JSVM-API获取堆快照及监控堆内存阈值

使用JSVM-API获取堆快照及监控堆内存阈值

简介

从API版本26.0.0开始,JSVM-API提供了堆内存管理相关的核心能力,包含获取原始堆快照和监控堆内存阈值两类关键接口:

  • OH_JSVM_TakeRawHeapSnapshot:获取当前JS虚拟机(VM)的原始堆快照(二进制格式)并输出到指定流,可用于堆内存分析、内存泄漏定位、调试等场景。
  • OH_JSVM_SetHeapThresholdCallback:为VM注册堆内存阈值回调函数,当堆内存使用量达到指定阈值时自动触发回调。
  • OH_JSVM_ClearHeapThresholdCallback:移除已注册的堆内存阈值回调函数,释放相关资源。

开发者可通过这些接口实现堆内存的全生命周期监控、快照采集与分析,辅助优化内存使用效率、排查内存相关问题。

基本概念

原始堆快照(Raw Heap Snapshot)

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部分代码

收起
自动换行
深色代码主题
复制
  1. #include <iostream>
  2. #include <fstream>
  3. #include <thread>
  4. #include <chrono>
  5. #include <unistd.h>
  6. #include <sys/types.h>
  7. #include "napi/native_api.h"
  8. #include "hilog/log.h"
  9. #include "ark_runtime/jsvm.h"
  10. #define LOG_DOMAIN 0x3200
  11. #define LOG_TAG "APP"
  12. static int g_aa = 0;
  13. static bool g_heapThresholdCalled = false;
  14. static uint64_t g_triggeredThreshold = 0;
  15. static void* g_callbackUserData = nullptr;
  16. static bool g_snapshotGenerated = false;
  17. static constexpr int SLEEP_TIME_MS = 100;
  18. static constexpr uint64_t THRESHOLD_SIZE = 1024 * 1024;
  19. static constexpr int TEST_DATA_VALUE = 0x12345678;
  20. bool SnapshotStreamCallback(const char* data, int size, void* streamData)
  21. {
  22. std::FILE* file = reinterpret_cast<std::FILE*>(streamData);
  23. if (file) {
  24. size_t written = std::fwrite(data, 1, size, file);
  25. return written == static_cast<size_t>(size);
  26. }
  27. return true;
  28. }
  29. void OnHeapThresholdReached(JSVM_VM vm, uint64_t threshold, void* data)
  30. {
  31. OH_LOG_INFO(LOG_APP, "== Heap threshold reached ==");
  32. OH_LOG_INFO(LOG_APP, "Threshold: %{public}lu bytes", threshold);
  33. OH_LOG_INFO(LOG_APP, "User data: %{public}d", *static_cast<int*>(data));
  34. g_heapThresholdCalled = true;
  35. g_triggeredThreshold = threshold;
  36. g_callbackUserData = data;
  37. if (!g_snapshotGenerated) {
  38. g_snapshotGenerated = true;
  39. pid_t pid = fork();
  40. if (pid < 0) {
  41. OH_LOG_ERROR(LOG_APP, "fork failed");
  42. } else if (pid == 0) {
  43. std::FILE* file = std::fopen(
  44. "/data/storage/el2/base/temp/threshold.rawheap", "wb");
  45. OH_JSVM_TakeRawHeapSnapshot(vm, SnapshotStreamCallback, file);
  46. std::fflush(file);
  47. fclose(file);
  48. _exit(0);
  49. }
  50. }
  51. }
  52. static void ResetTestState()
  53. {
  54. g_heapThresholdCalled = false;
  55. g_triggeredThreshold = 0;
  56. g_callbackUserData = nullptr;
  57. g_snapshotGenerated = false;
  58. }
  59. static void TestSetHeapThresholdCallback(JSVM_VM vm, uint64_t threshold, int* testData)
  60. {
  61. JSVM_Status addStatus = OH_JSVM_SetHeapThresholdCallback(
  62. vm, threshold, OnHeapThresholdReached, testData);
  63. if (addStatus == JSVM_OK) {
  64. OH_LOG_INFO(LOG_APP, "Set heap threshold callback success");
  65. }
  66. JSVM_Status addRepeatStatus = OH_JSVM_SetHeapThresholdCallback(
  67. vm, threshold, OnHeapThresholdReached, testData);
  68. if (addRepeatStatus == JSVM_INVALID_ARG) {
  69. OH_LOG_INFO(LOG_APP, "Set repeated callback failed (expected)");
  70. }
  71. JSVM_Status addInvalidStatus = OH_JSVM_SetHeapThresholdCallback(
  72. vm, 0, OnHeapThresholdReached, testData);
  73. if (addInvalidStatus == JSVM_INVALID_ARG) {
  74. OH_LOG_INFO(LOG_APP, "Set callback with 0 threshold failed (expected)");
  75. }
  76. }
  77. static void TestClearHeapThresholdCallback(JSVM_VM vm, uint64_t threshold, int* testData)
  78. {
  79. JSVM_Status removeStatus = OH_JSVM_ClearHeapThresholdCallback(
  80. vm, threshold, OnHeapThresholdReached, testData);
  81. if (removeStatus == JSVM_OK) {
  82. OH_LOG_INFO(LOG_APP, "Clear heap threshold callback success");
  83. }
  84. JSVM_Status removeRepeatStatus = OH_JSVM_ClearHeapThresholdCallback(
  85. vm, threshold, OnHeapThresholdReached, testData);
  86. if (removeRepeatStatus == JSVM_INVALID_ARG) {
  87. OH_LOG_INFO(LOG_APP, "Clear repeated callback failed (expected)");
  88. }
  89. JSVM_Status removeMismatchStatus = OH_JSVM_ClearHeapThresholdCallback(
  90. vm, 999999, OnHeapThresholdReached, testData);
  91. if (removeMismatchStatus == JSVM_INVALID_ARG) {
  92. OH_LOG_INFO(LOG_APP, "Clear mismatch threshold callback failed (expected)");
  93. }
  94. }
  95. static void RunAllocScript(JSVM_Env env)
  96. {
  97. const char* allocJs = R"JS(
  98. var holder = [];
  99. for (let i = 0; i < 10000; i++) {
  100. holder.push(new Uint8Array(1024));
  101. }
  102. )JS";
  103. JSVM_Value jsSrc;
  104. JSVM_Value result1;
  105. JSVM_Script script;
  106. OH_JSVM_CreateStringUtf8(env, allocJs, JSVM_AUTO_LENGTH, &jsSrc);
  107. OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script);
  108. OH_JSVM_RunScript(env, script, &result1);
  109. }
  110. static bool CheckTestResult(uint64_t threshold, int* testData)
  111. {
  112. bool testSuccess = (g_heapThresholdCalled &&
  113. g_triggeredThreshold == threshold &&
  114. g_callbackUserData == testData &&
  115. g_snapshotGenerated);
  116. if (testSuccess) {
  117. OH_LOG_INFO(LOG_APP, "Heap management test: SUCCESS");
  118. } else {
  119. OH_LOG_ERROR(LOG_APP, "Heap management test: FAILED");
  120. }
  121. return testSuccess;
  122. }
  123. static JSVM_Value HeapMgmtTest(JSVM_Env env, JSVM_CallbackInfo info)
  124. {
  125. ResetTestState();
  126. JSVM_VM vm;
  127. OH_JSVM_GetVM(env, &vm);
  128. int testData = TEST_DATA_VALUE;
  129. uint64_t threshold = THRESHOLD_SIZE;
  130. std::FILE* file = std::fopen(
  131. "/data/storage/el2/base/temp/take.rawheap", "wb");
  132. JSVM_Status snapshotStatus = OH_JSVM_TakeRawHeapSnapshot(
  133. vm, SnapshotStreamCallback, file);
  134. if (snapshotStatus == JSVM_INVALID_ARG) {
  135. OH_LOG_ERROR(LOG_APP, "Take raw heap snapshot failed (invalid arg)");
  136. }
  137. TestSetHeapThresholdCallback(vm, threshold, &testData);
  138. RunAllocScript(env);
  139. OH_JSVM_MemoryPressureNotification(env, JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL);
  140. std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS));
  141. TestClearHeapThresholdCallback(vm, threshold, &testData);
  142. bool testSuccess = CheckTestResult(threshold, &testData);
  143. JSVM_Value result;
  144. OH_JSVM_GetBoolean(env, testSuccess, &result);
  145. return result;
  146. }
  147. static JSVM_CallbackStruct param[] = {
  148. {.data = nullptr, .callback = HeapMgmtTest},
  149. };
  150. static JSVM_CallbackStruct* method = param;
  151. static JSVM_PropertyDescriptor descriptor[] = {
  152. {"heapMgmtTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
  153. };
  154. const char* SRC_CALL_NATIVE = R"JS(heapMgmtTest();)JS";

执行结果

在LOG中输出下面结果,同时生成take.rawheap和threshold.rawheap二进制快照文件:

收起
自动换行
深色代码主题
复制
  1. Set heap threshold callback success
  2. Set repeated callback failed (expected)
  3. Set callback with 0 threshold failed (expected)
  4. == Heap threshold reached ==
  5. Threshold: 1048576 bytes
  6. User data: 305419896
  7. Clear heap threshold callback success
  8. Clear repeated callback failed (expected)
  9. Clear mismatch threshold callback failed (expected)
  10. Heap management test: SUCCESS

注意事项

  1. OH_JSVM_TakeRawHeapSnapshot
    • vm/stream参数为NULL时,返回JSVM_INVALID_ARG;其他场景均返回JSVM_OK;
    • 快照流回调需避免长时间阻塞,否则会阻塞VM线程;
    • 频繁调用会产生大量二进制文件,建议按需采集并及时清理。
  2. OH_JSVM_SetHeapThresholdCallback
    • 阈值需满足:0 < threshold ≤ heapSizeLimit(heapSizeLimit来自JSVM_HeapStatistics),否则返回JSVM_INVALID_ARG;
    • vm或callback参数为NULL时,返回JSVM_INVALID_ARG;
    • VM已注册回调时重复注册,返回JSVM_INVALID_ARG;
    • 接口非线程安全,必须在VM运行的线程调用;
    • 阈值检查在GC期间进行,回调在同一线程同步调用;回调执行期间会跳过阈值检查,避免递归触发;回调返回后若堆使用量仍≥阈值,下次GC会再次触发,无需重新注册。
  3. OH_JSVM_ClearHeapThresholdCallback
    • vm/callback参数为NULL时,或(阈值+回调+用户数据)与已注册信息不匹配时,返回JSVM_INVALID_ARG;
    • 接口非线程安全,必须在VM运行的线程调用;
    • 回调执行期间可移除自身,并重新注册新的阈值回调(需保证参数合法)。