使用JSVM-API感知JSVM引擎生命周期管理
========================

#### 简介

JSVM-API提供了注册回调函数的能力，用于监测JavaScript虚拟机的内存GC。开发者可以在垃圾回收前后添加自定义逻辑，从而在垃圾回收时执行优化、调试或性能监控操作。

#### 基本概念

在JavaScript中，内存的垃圾回收是自动进行的，用户并不直接感知JavaScript虚拟机的GC行为。每次GC执行之前，JS引擎会先进入一个"Prologue"阶段。每次GC执行之后，JS引擎会进入一个"Epilogue"阶段。"Prologue"阶段是GC的初始阶段，主要目标是做一些准备工作，以确保垃圾回收能够顺利进行。"Epilogue"阶段则是垃圾回收的最终清理和整理，确保内存恢复到一个正常的状态，并为下一次分配做好准备。在这两个阶段，JS引擎会分别调用用户提前注册的函数。用户可以在"Prologue"阶段所执行的注册函数中暂停某些任务、记录内存使用情况、执行性能调优等。在"Epilogue"阶段所执行的注册函数中，也可以去记录GC后的内存状态、启动后续的任务等等。

JSVM-API提供了OH\_JSVM\_AddHandlerForGC接口，可以在VM中注册回调函数。通过传入JSVM\_CB\_TRIGGER\_BEFORE\_GC来控制回调函数在"Prologue"阶段执行；通过传入JSVM\_CB\_TRIGGER\_AFTER\_GC来控制回调函数在"Epilogue"阶段执行。通过OH\_JSVM\_RemoveHandlerForGC，可以从VM中移除注册过的回调函数。

#### 接口说明

| 接口 | 功能说明 |
| :-- | :-- |
| OH\_JSVM\_AddHandlerForGC | 用于向VM中注册回调函数 |
| OH\_JSVM\_RemoveHandlerForGC | 用于从VM中移除注册过的回调函数 |

#### 使用示例

JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-jsvm-process)，本文仅对接口对应C++相关代码进行展示。

#### \[h2\]OH\_JSVM\_AddHandlerForGC & OH\_JSVM\_RemoveHandlerForGC

可以多次调用OH\_JSVM\_AddHandlerForGC向VM注册回调函数，所有注册的回调函数都会生效。注册时，以回调函数指针和native-data作为键。如果多次注册存在相同的键，则视为无效注册，并返回JSVM\_INVALID\_ARG错误码。在相同触发条件下，回调函数的回调顺序与注册顺序不严格一致。

通过OH\_JSVM\_RemoveHandlerForGC可以从VM中移除注册过的回调函数。重复移除具有相同key的回调函数，则会判定为无效移除，并返回JSVM\_INVALID\_ARG错误码。

**cpp部分代码**

// hello.cpp
#include <iostream>

static bool before\_flag1 = false;
static bool before\_flag2 = false;
static bool after\_flag1 = false;
static bool after\_flag2 = false;

void OnBeforeGC(JSVM\_VM vm, JSVM\_GCType gcType, JSVM\_GCCallbackFlags flags, void \*data)
{
    OH\_LOG\_INFO(LOG\_APP, "== before GC ==");
    OH\_LOG\_INFO(LOG\_APP, "gc type: %{public}d", gcType);
    OH\_LOG\_INFO(LOG\_APP, "gc flag: %{public}d", flags);
    before\_flag1 = true;
}

void OnBeforeGC2(JSVM\_VM vm, JSVM\_GCType gcType, JSVM\_GCCallbackFlags flags, void \*data)
{
    OH\_LOG\_INFO(LOG\_APP, "== before GC2 ==");
    OH\_LOG\_INFO(LOG\_APP, "gc type: %{public}d", gcType);
    OH\_LOG\_INFO(LOG\_APP, "gc flag: %{public}d", flags);
    OH\_LOG\_INFO(LOG\_APP, "data: %{public}d", \*(int\*)data);
    if (\*(int\*)data == 2024) {
        before\_flag2 = true;
    }
}

void OnAfterGC(JSVM\_VM vm, JSVM\_GCType gcType, JSVM\_GCCallbackFlags flags, void \*data)
{
    after\_flag1 = true;
}

void OnAfterGC2(JSVM\_VM vm, JSVM\_GCType gcType, JSVM\_GCCallbackFlags flags, void \*data)
{
    after\_flag2 = true;
}

void OnAfterGC3(JSVM\_VM vm, JSVM\_GCType gcType, JSVM\_GCCallbackFlags flags, void \*data)
{
    after\_flag2 = true;
}

static JSVM\_Value TriggerGC(JSVM\_Env env, JSVM\_CallbackInfo info)
{
    bool remove\_repeated = false;
    bool remove\_notAdded = false;
    bool add\_repeated = false;
    before\_flag1 = false;
    before\_flag2 = false;
    after\_flag1 = false;
    after\_flag2 = false;
    JSVM\_VM vm;
    OH\_JSVM\_GetVM(env, &vm);
    // 设置两个回调函数，在GC执行之前触发回调
    int data = 2024;
    JSVM\_CALL(OH\_JSVM\_AddHandlerForGC(vm, JSVM\_CB\_TRIGGER\_BEFORE\_GC, OnBeforeGC, JSVM\_GC\_TYPE\_ALL, NULL));
    JSVM\_CALL(OH\_JSVM\_AddHandlerForGC(vm, JSVM\_CB\_TRIGGER\_BEFORE\_GC, OnBeforeGC2, JSVM\_GC\_TYPE\_ALL, (void\*)(&data)));
    // 设置两个回调函数，在GC执行之后触发回调
    JSVM\_CALL(OH\_JSVM\_AddHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC, JSVM\_GC\_TYPE\_ALL, NULL));
    JSVM\_CALL(OH\_JSVM\_AddHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC2, JSVM\_GC\_TYPE\_ALL, NULL));
    // (OnAfterGC2, NULL)的组合已经注册过了，重复注册为无效行为
    if (OH\_JSVM\_AddHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC2, JSVM\_GC\_TYPE\_ALL, NULL) == JSVM\_INVALID\_ARG) {
        add\_repeated = true;
    }
    // 移除OnAfter2回调函数
    JSVM\_CALL(OH\_JSVM\_RemoveHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC2, NULL));
    // 重复移除OnAfter2属于无效用法
    if (OH\_JSVM\_RemoveHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC2, NULL) == JSVM\_INVALID\_ARG) {
        remove\_repeated = true;
    }
    // 移除从未设置过的函数属于无效用法
    if (OH\_JSVM\_RemoveHandlerForGC(vm, JSVM\_CB\_TRIGGER\_AFTER\_GC, OnAfterGC3, NULL) == JSVM\_INVALID\_ARG) {
        remove\_notAdded = true;
    }
    // 通知引擎当前存在比较大的内存压力，能大概率触发JS引擎的GC流程。
    JSVM\_CALL(OH\_JSVM\_MemoryPressureNotification(env, JSVM\_MEMORY\_PRESSURE\_LEVEL\_CRITICAL));
    if ((before\_flag1) &&
        (before\_flag2) &&
        (after\_flag1) &&
        (!after\_flag2) &&
        (remove\_repeated) &&
        (remove\_notAdded) &&
        (add\_repeated)) {
        OH\_LOG\_INFO(LOG\_APP, "JSVM Trigger GC: success");
    } else {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM Trigger GC: failed");
    }
    JSVM\_Value checked;
    OH\_JSVM\_GetBoolean(env, true, &checked);
    return checked;
}

static JSVM\_CallbackStruct param\[\] = {
    {.data = nullptr, .callback = TriggerGC},
};
static JSVM\_CallbackStruct \*method = param;

static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"triggerGC", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

**样例测试JS**

const char \*srcCallNative = R"JS(triggerGC();)JS";

**执行结果**

在LOG中输出下面结果：

\== before GC ==
gc type: 4
gc flag: 4
== before GC2 ==
gc type: 4
gc flag: 4
data: 2024
JSVM Trigger GC: success