如何高效的使用N-API开发Native模块?

简介

N-API 是 Node.js Addon Programming Interface 的缩写,是 Node.js 提供的一组 C++ API,封装了V8 引擎的能力,用于编写 Node.js 的 Native 扩展模块。通过 N-API,开发者可以使用 C++ 编写高性能的 Node.js 模块,同时保持与 Node.js 的兼容性。

Node.js 官网中已经给出 N-API 接口基础能力的介绍,同时,方舟 ArkTS 运行时提供的 N-API 接口,封装了方舟引擎的能力,在功能上与 Node.js 社区保持一致,这里不再赘述。

本文将结合应用开发场景,分别从对象生命周期管理、跨语言调用开销、异步操作和线程安全四个角度出发,给出安全、高效的 N-API 开发指导。

对象生命周期管理

在进行 N-API 调用时,引擎堆中对象的句柄 handle 会作为 napi_value 返回,对象的生命周期由这些句柄控制。对象的句柄会与一个 scope 保持一致,默认情况下,对象当前所在 native 方法是 handle 的 scope。在应用 native 模块实际开发过程中,需要对象有比当前所在 native 方法更短或更长的 scope。本文描述了管理对象生命周期的 N-API 接口,开发者通过这些接口可以合理的管理对象生命周期,满足业务诉求。

缩短对象生命周期

合理使用 napi_open_handle_scope 和 napi_close_handle_scope 管理 napi_value 的生命周期,做到生命周期最小化,避免发生内存泄漏问题。

例如,考虑一个具有 for 循环的方法,在该循环中遍历获取大型数组的元素,示例代码如下:

for (int i = 0; i < 1000000; i++) {
   
 napi_value result;
 napi_status status = napi_get_element(env, object, i, &result);
 if (status != napi_ok) {
   
  break;
 }
 // do something with element
}

在 for 循环中会创建大量的 handle,消耗大量资源。为了减小内存开销,N-API 提供创建局部 scope 的能力,在局部 scope 中间所创建 handle 的生命周期将与局部 scpoe 保持一致。一旦不再需要这些 handle,就可以直接关闭局部 scope。

  • 打开和关闭 scope 的方法为 napi_open_handle_scope 和 napi_close_handle_scope;
  • N-API 中 scope 的层次结构是一个嵌套的层次结构,任何时候只有一个存活的 scope,所有新创建的 handle 都将在该 scope 处于存活状态时与之关联;
  • scope 必须按打开的相反顺序关闭,在 native 方法中创建的所有 scope 必须在该方法返回之前关闭。

例如,使用下面的方法,可以确保在循环中,最多只有一个句柄是有效的:

// 在for循环中频繁调用napi接口创建js对象时,要加handle_scope及时释放不再使用的资源;
// 下面例子中,每次循环结束局部变量res的生命周期已结束,因此加scope及时释放其持有的js对象,防止内存泄漏。
for (int i = 0; i < 1000000; i++) {
   
    napi_handle_scope scope;
    napi_status status = napi_open_handle_scope(env, &scope);
    if (status != napi_ok) {
   
        break;
    }
    napi_value result;
    status = napi_get_element(env, object, i, &result);
    if (status != napi_ok) {
   
        break;
    }
    // do something with element
    status = napi_close_handle_scope(env, scope);
    if (status != napi_ok) {
   
        break;
    }
}

存在一些场景,某些对象的生命周期需要大于对象本身所在区域的生命周期,例如嵌套循环场景。开发者可以通过 napi_open_escapable_handle_scope 与 napi_close_escapable_handle_scope 管理对象的生命周期,在此期间定义的对象的生命周期将与父作用域的生命周期保持一致。

延长对象生命周期

开发者可以通过创建 napi_ref 来延长 napi_value 对象的生命周期,通过 napi_create_reference 创建的对象需要用户手动调用 napi_delete_reference 释放,否则可能造成内存泄漏。

使用案例1:保存 napi_value

通过 napi_define_class 创建一个 constructor 并保存下来,后续可以通过保存的 constructor 调用 napi_new_instance 来创建实例。但是,如果 constructor 是以 napi_value 的形式保存下来,一旦超过了 native 方法的 scope,这个 constructor 就会被析构,后续再使用就会造成野指针。推荐写法如下:

  • 1、开发者可以改用 napi_ref 的形式把 constructor 保存下来;
  • 2、由开发者自己管理 constructor 对象的生命周期,不受 native 方法的 scope 限制。
// 1、开发者可以改用 napi_ref 的形式把 constructor 保存下来
static napi_value TestDefineClass(napi_env env,
                                  napi_callback_info info) {
   
  napi_status status;
  napi_value result, return_value;

  napi_property_descriptor property_descriptor = {
   
    "TestDefineClass",
    NULL,
    TestDefineClass,
    NULL,
    NULL,
    NULL,
    napi_enumerable | napi_static,
    NULL};

  NODE_API_CALL(env, napi_create_object(env, &return_value));

  status = napi_define_class(NULL,
                             "TrackedFunction",
                             NAPI_AUTO_LENGTH,
                             TestDefineClass,
                             NULL,
                             1,
                             &property_descriptor,
                             &result);
  SaveConstructor(env, result);
  ...
}
// 2、由开发者自己管理 constructor 对象的生命周期
napi_status SaveConstructor(napi_env env, napi_value constructor) {
   
    return napi_create_reference(env, constructor, 1, &g_constructor);
};

napi_status GetConstructor(napi_env env) {
   
    napi_value constructor;
    return 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值