1 # 使用JSVM-API接口进行生命周期相关开发
2 
3 ## 简介
4 
5 在JSVM-API中,JSVM_Value是一个表示JavaScript值的抽象类型,它可以表示任何JavaScript值,包括基本类型(如数字、字符串、布尔值)和对象类型(如数组、函数、对象等)。
6 JSVM_Value的生命周期与其在JavaScript中的对应值的生命周期紧密相关。当JavaScript值被垃圾回收时,与之关联的JSVM_Value也将不再有效。重要的是不要在JavaScript值不再存在时尝试使用JSVM_Value。
7 
8 框架层的scope通常用于管理JSVM_Value的生命周期。在JSVM-API中,可以使用OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope函数来创建和销毁scope。通过在scope内创建JSVM_Value,可以确保在scope结束时自动释放JSVM_Value,避免内存泄漏。
9 
10 JSVM_Ref是一个JSVM-API类型,用于管理JSVM_Value的生命周期。JSVM_Ref允许您在JSVM_Value的生命周期内保持对其的引用,即使它已经超出了其原始上下文的范围。这使得您可以在不同的上下文中共享JSVM_Value,并确保在不再需要时正确释放其内存。
11 
12 合理使用OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope管理JSVM_Value的生命周期,做到生命周期最小化,避免发生内存泄漏问题。
13 
14 每个JSVM_Value属于特定的HandleScope,HandleScope通过OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope来建立和关闭,HandleScope关闭后,所属的JSVM_Value就会自动释放。
15 
16 ## 基本概念
17 
18 JSVM-API提供了一组功能,使开发人员能够在JSVM-API模块中创建和操作JavaScript对象,管理引用和生命周期,并注册垃圾回收回调函数等。下面是一些基本概念:
19 
20 - **作用域**:用于创建一个范围,在范围内声明的引用在范围外部将不再生效。JSVM-API提供了创建、关闭普通和可逃逸的作用域的函数。
21 - **引用管理**:JSVM-API提供函数来创建、删除和管理对象的引用,以延长对象的生命周期,并避免在使用对象时发生内存泄漏。
22 - **可逃逸的作用域**:允许在创建的作用域中声明的对象返回到父作用域,通过OH_JSVM_OpenEscapableHandleScope和OH_JSVM_CloseEscapableHandleScope进行管理。
23 - **垃圾回收回调**:允许注册回调函数,以便在JavaScript对象被垃圾回收时执行特定的清理操作。
24 
25 这些基本概念使开发人员能够在JSVM-API模块中安全且有效地操作JavaScript对象,并确保正确管理对象的生命周期。
26 
27 ## 接口说明
28 
29 | 接口                       | 功能说明                       |
30 |----------------------------|--------------------------------|
31 | OH_JSVM_OpenHandleScope     | 打开一个Handle scope,确保scope范围内的JSVM_Value不被GC回收 |
32 | OH_JSVM_CloseHandleScope    | 关闭Handle scope|
33 | OH_JSVM_OpenEscapableHandleScope     | 打开一个新的scope逃逸Handle scope,在关闭该scope之前创建的对象与父作用域有相同的生命周期 |
34 | OH_JSVM_CloseEscapableHandleScope    | 关闭一个scope,在此scope范围外创建的对象不受父作用域保护 |
35 | OH_JSVM_EscapeHandle         | 将JavaScript对象的句柄提升到外部作用域,确保在外部作用域中可以持续地使用该对象 |
36 | OH_JSVM_CreateReference      | 以指定的引用计数为JavaScript对象创建一个新的引用,该引用将指向传入的对象,引用允许在不同的上下文中使用和共享对象,并且可以有效地跟踪对象的生命周期 |
37 | OH_JSVM_DeleteReference      | 释放由OH_JSVM_CreateReference创建的引用,确保对象在不再被使用时能够被正确地释放和回收,避免内存泄漏 |
38 | OH_JSVM_ReferenceRef         | 增加由OH_JSVM_CreateReference创建的引用的引用计数,以确保对象在有引用时不会被提前释放 |
39 | OH_JSVM_ReferenceUnref       | 减少引用计数,用于管理引用计数。|
40 | OH_JSVM_GetReferenceValue   | 减少由OH_JSVM_CreateReference创建的引用的引用计数,以确保没有任何引用指向该对象时能正确地释放和回收 |
41 | OH_JSVM_AddFinalizer          | 为对象添加JSVM_Finalize回调,以便在JavaScript对象被垃圾回收时调用来释放原生对象。|
42 
43 ## 使用示例
44 
45 JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。
46 
47 ### OH_JSVM_OpenHandleScope、OH_JSVM_CloseHandleScope
48 
49 通过接口OH_JSVM_OpenHandleScope创建一个上下文环境使用。需要使用OH_JSVM_CloseHandleScope进行关闭。用于管理JavaScript对象的生命周期确保在JSVM-API模块代码处理JavaScript对象时能够正确地管理其句柄,以避免出现垃圾回收相关的问题。
50 
51 cpp部分代码
52 
53 ```cpp
54 // OH_JSVM_OpenHandleScope、OH_JSVM_CloseHandleScope的三种样例方法
55 static JSVM_Value HandleScopeFor(JSVM_Env env, JSVM_CallbackInfo info) {
56     // 在for循环中频繁调用JSVM接口创建js对象时,要加handle_scope及时释放不再使用的资源。
57     // 下面例子中,每次循环结束局部变量res的生命周期已结束,因此加scope及时释放其持有的js对象,防止内存泄漏
58     constexpr uint32_t DIFF_VALUE_HUNDRED_THOUSAND = 10000;
59     JSVM_Value checked = nullptr;
60     for (int i = 0; i < DIFF_VALUE_HUNDRED_THOUSAND; i++) {
61         JSVM_HandleScope scope = nullptr;
62         JSVM_Status status = OH_JSVM_OpenHandleScope(env, &scope);
63         if (status != JSVM_OK || scope == nullptr) {
64             OH_JSVM_GetBoolean(env, false, &checked);
65             OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenHandleScope: failed");
66             return checked;
67         }
68         JSVM_Value res = nullptr;
69         OH_JSVM_CreateObject(env, &res);
70         status = OH_JSVM_CloseHandleScope(env, scope);
71         if (status != JSVM_OK) {
72             OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseHandleScope: failed");
73         }
74     }
75     OH_JSVM_GetBoolean(env, true, &checked);
76     OH_LOG_INFO(LOG_APP, "JSVM HandleScopeFor: success");
77     return checked;
78 }
79 
80 // HandleScopeFor注册回调
81 static JSVM_CallbackStruct param[] = {
82     {.callback = HandleScopeFor, .data = nullptr},
83 };
84 
85 static JSVM_CallbackStruct *method = param;
86 // HandleScopeFor方法别名,供JS调用
87 static JSVM_PropertyDescriptor descriptor[] = {
88     {"HandleScopeFor", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
89 };
90 ```
91 
92 预期输出
93 ```
94 JSVM HandleScopeFor: success
95 ```
96 
97 ### OH_JSVM_OpenEscapableHandleScope、OH_JSVM_CloseEscapableHandleScope、OH_JSVM_EscapeHandle
98 
99 通过接口 OH_JSVM_OpenEscapableHandleScope 创建出一个可逃逸的 handel scope,可将 1 个范围内声明的值返回到父作用域。创建的 scope 需使用 OH_JSVM_CloseEscapableHandleScope 进行关闭。OH_JSVM_EscapeHandle 将传入的 JavaScript 对象的生命周期提升到其父作用域。
100 通过上述接口可以更灵活的使用管理传入的 JavaScript 对象,特别是在处理跨作用域的值传递时非常有用。
101 
102 cpp 部分代码
103 
104 ```cpp
105 // OH_JSVM_OpenEscapableHandleScope、OH_JSVM_CloseEscapableHandleScope、OH_JSVM_EscapeHandle的样例方法
106 static JSVM_Value EscapableHandleScopeTest(JSVM_Env env, JSVM_CallbackInfo info)
107 {
108     // 创建一个可逃逸的句柄作用域
109     JSVM_EscapableHandleScope scope = nullptr;
110     JSVM_Status status = OH_JSVM_OpenEscapableHandleScope(env, &scope);
111     if (status != JSVM_OK) {
112         OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenEscapableHandleScope: failed");
113         return nullptr;
114     }
115     // 在可逃逸的句柄作用域内创建一个obj
116     JSVM_Value obj;
117     OH_JSVM_CreateObject(env, &obj);
118     // 在对象中添加属性
119     JSVM_Value value = nullptr;
120     OH_JSVM_CreateStringUtf8(env, "Test jsvm_escapable_handle_scope", JSVM_AUTO_LENGTH, &value);
121     OH_JSVM_SetNamedProperty(env, obj, "name", value);
122     // 调用OH_JSVM_EscapeHandle将对象逃逸到作用域之外
123     JSVM_Value escapedObj = nullptr;
124     OH_JSVM_EscapeHandle(env, scope, obj, &escapedObj);
125     // 关闭可逃逸的句柄作用域,清理资源
126     status = OH_JSVM_CloseEscapableHandleScope(env, scope);
127     if (status != JSVM_OK) {
128         OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseEscapableHandleScope: failed");
129         return nullptr;
130     }
131     // 此时的escapedObj已逃逸,可以在作用域外继续使用escapedObj
132     bool result = false;
133     OH_JSVM_CreateStringUtf8(env, "name", JSVM_AUTO_LENGTH, &value);
134     OH_JSVM_HasProperty(env, escapedObj, value, &result);
135     if (result) {
136         OH_LOG_INFO(LOG_APP, "JSVM EscapableHandleScopeTest: success");
137     }
138     return escapedObj;
139 }
140 
141 // EscapableHandleScopeTest注册回调
142 static JSVM_CallbackStruct param[] = {
143     {.callback = EscapableHandleScopeTest, .data = nullptr},
144 };
145 static JSVM_CallbackStruct *method = param;
146 // EscapableHandleScopeTest方法别名,供JS调用
147 static JSVM_PropertyDescriptor descriptor[] = {
148     {"escapableHandleScopeTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
149 };
150 
151 const char *srcCallNative = "escapableHandleScopeTest()";
152 ```
153 
154 预期输出
155 
156 ```
157 JSVM EscapableHandleScopeTest: success
158 ```
159 
160 ### OH_JSVM_CreateReference、OH_JSVM_DeleteReference、OH_JSVM_GetReferenceValue
161 
162 1. 调用 OH_JSVM_CreateReference 为 JavaScript 变量创建一个引用,以延长其生命周期。
163 2. 调用 OH_JSVM_GetReferenceValue 获取与引用关联的 JavaScript 变量。
164 3. 调用 OH_JSVM_DeleteReference 删除传入的引用。
165 
166 调用者需要自己管理引用生命周期,引用有效期间 JavaScript 变量不会被垃圾回收处理。
167 
168 ### OH_JSVM_ReferenceRef、OH_JSVM_ReferenceUnref
169 
170 增加/减少 传入的引用的引用计数,并获取新的计数。当引用计数被置为 0 后,对于可以被设置为弱引用的 JavaScript 类型(对象、函数、外部变量),引用将被置为弱引用,在垃圾回收机制认为必要的时候该变量会被回收,当变量被回收后,调用 OH_JSVM_GetReferenceValue 会获得 JavaScript NULL;对于不可被置为弱引用的 JavaScript 类型,该引用会被清除,调用 OH_JSVM_GetReferenceValue 会获得 JavaScript NULL。
171 
172 cpp部分代码
173 
174 ```cpp
175 static JSVM_Value UseReference(JSVM_Env env, JSVM_CallbackInfo info)
176 {
177     // 创建 JavaScript 对象
178     JSVM_Value obj = nullptr;
179     OH_JSVM_CreateObject(env, &obj);
180     JSVM_Value value = nullptr;
181     OH_JSVM_CreateStringUtf8(env, "UseReference", JSVM_AUTO_LENGTH, &value);
182     OH_JSVM_SetNamedProperty(env, obj, "name", value);
183 
184     JSVM_Ref g_ref = nullptr;
185     // 创建对JavaScript对象的引用
186     JSVM_Status status = OH_JSVM_CreateReference(env, obj, 1, &g_ref);
187     if (status != JSVM_OK) {
188         return nullptr;
189     }
190 
191     // 增加传入引用的引用计数并返回生成的引用计数
192     uint32_t result;
193     OH_JSVM_ReferenceRef(env, g_ref, &result);
194     OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceRef, count = %{public}d.", result);
195     if (result != 2) {
196         OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_ReferenceRef: failed");
197         return nullptr;
198     }
199 
200     //  减少传入引用的引用计数并返回生成的引用计数
201     uint32_t num;
202     OH_JSVM_ReferenceUnref(env, g_ref, &num);
203     OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceUnref, count = %{public}d.", num);
204     if (num != 1) {
205         return nullptr;
206     }
207 
208     JSVM_Value object = nullptr;
209     // 通过调用OH_JSVM_GetReferenceValue获取引用的JavaScript对象
210     status = OH_JSVM_GetReferenceValue(env, g_ref, &object);
211     if (status != JSVM_OK) {
212         OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_GetReferenceValue: failed");
213         return nullptr;
214     }
215 
216     // 不再使用引用,通过调用OH_JSVM_DeleteReference删除对JavaScript对象的引用
217     status = OH_JSVM_DeleteReference(env, g_ref);
218     if (status != JSVM_OK) {
219         OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_DeleteReference: failed");
220         return nullptr;
221     }
222 
223     // 将获取到的对象返回
224     OH_LOG_INFO(LOG_APP, "JSVM UseReference success");
225     return object;
226 }
227 
228 // CreateReference、UseReference、DeleteReference注册回调
229 static JSVM_CallbackStruct param[] = {
230     {.callback = UseReference, .data = nullptr},
231 };
232 static JSVM_CallbackStruct *method = param;
233 // CreateReference、UseReference、DeleteReference方法别名,供JS调用
234 static JSVM_PropertyDescriptor descriptor[] = {
235     {"useReference", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
236 };
237 
238 const char *srcCallNative = "useReference()";
239 ```
240 
241 预期结果
242 
243 ```
244 JSVM OH_JSVM_ReferenceRef, count = 2.
245 JSVM OH_JSVM_ReferenceUnref, count = 1.
246 JSVM UseReference success
247 ```
248 
249 ### OH_JSVM_AddFinalizer
250 为 JavaScript 对象添加 JSVM_Finalize 回调,当 JavaScript 对象被垃圾回收时执行函数回调,该接口通常被用于释放与 JavaScript 对象相关的原生对象。如果传入的参数类型不是 JavaScript 对象,该接口调用失败并返回错误码。
251 Finalizer 方法被注册后无法取消,如果在调用 OH_JSVM_DestroyEnv 前均未被执行,则在 OH_JVSM_DestroyEnv 时执行。
252 
253 cpp 部分代码
254 
255 ```cpp
256 static int AddFinalizer(JSVM_VM vm, JSVM_Env env) {
257     // 打开 handlescope
258     JSVM_HandleScope handleScope;
259     CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope));
260     // 创建 object 并设置回调
261     JSVM_Value obj;
262     CHECK_RET(OH_JSVM_CreateObject(env, &obj));
263     CHECK_RET(OH_JSVM_AddFinalizer(
264         env, obj, nullptr,
265         [](JSVM_Env env, void *data, void *hint) -> void {
266             // Finalizer 方法,可在该方法中清理 Native 对象
267             OH_LOG_INFO(LOG_APP, "JSVM: finalizer called.");
268         },
269         nullptr, nullptr));
270     OH_LOG_INFO(LOG_APP, "JSVM: finalizer added.");
271     // 关闭 handlescope,触发 GC,GC 时 Finalizer 会被调用
272     CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope));
273     OH_LOG_INFO(LOG_APP, "JSVM: before call gc.");
274     CHECK_RET(OH_JSVM_MemoryPressureNotification(env, JSVM_MemoryPressureLevel::JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL));
275     OH_LOG_INFO(LOG_APP, "JSVM: after call gc.");
276 
277     return 0;
278 }
279 
280 static void RunDemo(JSVM_VM vm, JSVM_Env env) {
281     if (AddFinalizer(vm, env) != 0) {
282         OH_LOG_INFO(LOG_APP, "Run PromiseRegisterHandler failed");
283     }
284 }
285 ```
286 
287 预期结果
288 ```
289 JSVM: finalizer added.
290 JSVM: before call gc.
291 JSVM: finalizer called.
292 JSVM: after call gc.
293 ```