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