• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用JSVM-API感知JSVM引擎生命周期管理
2<!--Kit: NDK Development-->
3<!--Subsystem: arkcompiler-->
4<!--Owner: @yuanxiaogou; @string_sz-->
5<!--Designer: @knightaoko-->
6<!--Tester: @test_lzz-->
7<!--Adviser: @fang-jinxu-->
8
9## 简介
10
11JSVM-API提供了注册回调函数的能力,用于监测JavaScript虚拟机的内存GC。开发者可以在垃圾回收前后添加自定义逻辑,从而在垃圾回收时执行优化、调试或性能监控操作。
12
13## 基本概念
14
15在JavaScript中,内存的垃圾回收是自动进行的,用户并不直接感知JavaScript虚拟机的GC行为。每次GC执行之前,JS引擎会先进入一个"Prologue"阶段。每次GC执行之后,JS引擎会进入一个"Epilogue"阶段。"Prologue"阶段是GC的初始阶段,主要目标是做一些准备工作,以确保垃圾回收能够顺利进行。"Epilogue"阶段则是垃圾回收的最终清理和整理,确保内存恢复到一个正常的状态,并为下一次分配做好准备。在这两个阶段,JS引擎会分别调用用户提前注册的函数。用户可以在"Prologue"阶段所执行的注册函数中暂停某些任务、记录内存使用情况、执行性能调优等。在"Epilogue"阶段所执行的注册函数中,也可以去记录GC后的内存状态、启动后续的任务等等。
16
17JSVM-API提供了OH_JSVM_AddHandlerForGC接口,可以在VM中注册回调函数。通过传入JSVM_CB_TRIGGER_BEFORE_GC来控制回调函数在"Prologue"阶段执行;通过传入JSVM_CB_TRIGGER_AFTER_GC来控制回调函数在"Epilogue"阶段执行。通过OH_JSVM_RemoveHandlerForGC,可以从VM中移除注册过的回调函数。
18
19## 接口说明
20
21| 接口                       | 功能说明                            |
22|----------------------------|-------------------------------------|
23| OH_JSVM_AddHandlerForGC         | 用于向VM中注册回调函数|
24| OH_JSVM_RemoveHandlerForGC   | 用于从VM中移除注册过的回调函数|
25
26## 使用示例
27
28JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。
29
30### OH_JSVM_AddHandlerForGC & OH_JSVM_RemoveHandlerForGC
31
32可以多次调用OH_JSVM_AddHandlerForGC向VM注册回调函数,所有注册的回调函数都会生效。注册时,以回调函数指针和native-data作为键。如果多次注册存在相同的键,则视为无效注册,并返回JSVM_INVALID_ARG错误码。在相同触发条件下,回调函数的回调顺序与注册顺序不严格一致。
33通过OH_JSVM_RemoveHandlerForGC可以从VM中移除注册过的回调函数。重复移除具有相同key的回调函数,则会判定为无效移除,并返回JSVM_INVALID_ARG错误码。
34
35**cpp部分代码**
36
37```cpp
38// hello.cpp
39#include <iostream>
40
41static bool before_flag1 = false;
42static bool before_flag2 = false;
43static bool after_flag1 = false;
44static bool after_flag2 = false;
45
46void OnBeforeGC(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void *data)
47{
48    OH_LOG_INFO(LOG_APP, "== before GC ==");
49    OH_LOG_INFO(LOG_APP, "gc type: %{public}d", gcType);
50    OH_LOG_INFO(LOG_APP, "gc flag: %{public}d", flags);
51    before_flag1 = true;
52}
53
54void OnBeforeGC2(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void *data)
55{
56    OH_LOG_INFO(LOG_APP, "== before GC2 ==");
57    OH_LOG_INFO(LOG_APP, "gc type: %{public}d", gcType);
58    OH_LOG_INFO(LOG_APP, "gc flag: %{public}d", flags);
59    OH_LOG_INFO(LOG_APP, "data: %{public}d", *(int*)data);
60    if (*(int*)data == 2024) {
61        before_flag2 = true;
62    }
63}
64
65void OnAfterGC(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void *data)
66{
67    after_flag1 = true;
68}
69
70void OnAfterGC2(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void *data)
71{
72    after_flag2 = true;
73}
74
75void OnAfterGC3(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void *data)
76{
77    after_flag2 = true;
78}
79
80static JSVM_Value TriggerGC(JSVM_Env env, JSVM_CallbackInfo info)
81{
82    bool remove_repeated = false;
83    bool remove_notAdded = false;
84    bool add_repeated = false;
85    before_flag1 = false;
86    before_flag2 = false;
87    after_flag1 = false;
88    after_flag2 = false;
89    JSVM_VM vm;
90    OH_JSVM_GetVM(env, &vm);
91    // 设置两个回调函数,在GC执行之前触发回调
92    int data = 2024;
93    JSVM_CALL(OH_JSVM_AddHandlerForGC(vm, JSVM_CB_TRIGGER_BEFORE_GC, OnBeforeGC, JSVM_GC_TYPE_ALL, NULL));
94    JSVM_CALL(OH_JSVM_AddHandlerForGC(vm, JSVM_CB_TRIGGER_BEFORE_GC, OnBeforeGC2, JSVM_GC_TYPE_ALL, (void*)(&data)));
95    // 设置两个回调函数,在GC执行之后触发回调
96    JSVM_CALL(OH_JSVM_AddHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC, JSVM_GC_TYPE_ALL, NULL));
97    JSVM_CALL(OH_JSVM_AddHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC2, JSVM_GC_TYPE_ALL, NULL));
98    // (OnAfterGC2, NULL)的组合已经注册过了,重复注册为无效行为
99    if (OH_JSVM_AddHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC2, JSVM_GC_TYPE_ALL, NULL) == JSVM_INVALID_ARG) {
100        add_repeated = true;
101    }
102    // 移除OnAfter2回调函数
103    JSVM_CALL(OH_JSVM_RemoveHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC2, NULL));
104    // 重复移除OnAfter2属于无效用法
105    if (OH_JSVM_RemoveHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC2, NULL) == JSVM_INVALID_ARG) {
106        remove_repeated = true;
107    }
108    // 移除从未设置过的函数属于无效用法
109    if (OH_JSVM_RemoveHandlerForGC(vm, JSVM_CB_TRIGGER_AFTER_GC, OnAfterGC3, NULL) == JSVM_INVALID_ARG) {
110        remove_notAdded = true;
111    }
112    // 通知引擎当前存在比较大的内存压力,能大概率触发JS引擎的GC流程。
113    JSVM_CALL(OH_JSVM_MemoryPressureNotification(env, JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL));
114    if ((before_flag1) &&
115        (before_flag2) &&
116        (after_flag1) &&
117        (!after_flag2) &&
118        (remove_repeated) &&
119        (remove_notAdded) &&
120        (add_repeated)) {
121        OH_LOG_INFO(LOG_APP, "JSVM Trigger GC: success");
122    } else {
123        OH_LOG_ERROR(LOG_APP, "JSVM Trigger GC: failed");
124    }
125    JSVM_Value checked;
126    OH_JSVM_GetBoolean(env, true, &checked);
127    return checked;
128}
129
130static JSVM_CallbackStruct param[] = {
131    {.data = nullptr, .callback = TriggerGC},
132};
133static JSVM_CallbackStruct *method = param;
134
135static JSVM_PropertyDescriptor descriptor[] = {
136    {"triggerGC", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
137};
138```
139**样例测试JS**
140```cpp
141const char *srcCallNative = R"JS(triggerGC();)JS";
142```
143**执行结果**
144
145在LOG中输出下面结果:
146```cpp
147== before GC ==
148gc type: 4
149gc flag: 4
150== before GC2 ==
151gc type: 4
152gc flag: 4
153data: 2024
154JSVM Trigger GC: success
155```