v8中如何防止对象被自动回收

How to prevent objects from being automatically recycled in v8

我需要在特定的时间触发用户的回调函数,但是触发回调函数时,对象已经被回收,无法使用。有没有办法保存对象而不被回收?触发回调后,想手动回收内存

LPVOID createCallbackFunc(void *lpMethod, Local<Function> *lpCB, Local<Context> *lpContext)
{
  /*
t_cb - 55                    - push rbp
014F0001- 48 8B EC              - mov rbp,rsp
014F0004- 48 81 EC 90010000     - sub rsp,00000190

014F000B- 48 89 4C 24 08        - mov [rsp+08],rcx
014F0010- 48 89 54 24 10        - mov [rsp+10],rdx
014F0015- 4C 89 44 24 18        - mov [rsp+18],r8
014F001A- 4C 89 4C 24 20        - mov [rsp+20],r9

014F001F- 48 B9 0000000000000000 - mov rcx,0000000000000000
014F0029- 48 BA 0000000000000000 - mov rdx,0000000000000000
014F0033- 4C 8D 44 24 08        - lea r8,[rsp+08]
014F0038- 4C 8D 4D 10           - lea r9,[rbp+10]

014F003C- 48 B8 F0ACAB14FE7F0000 - mov rax,user32.MessageBoxA
014F0046- FF D0                 - call rax

014F0048- 48 81 C4 90010000     - add rsp,00000190
014F004F- 48 8B E5              - mov rsp,rbp
014F0052- 5D                    - pop rbp
014F0053- C3                    - ret

*/

  string code_str = "55 48 8B EC 48 81 EC 90 01 00 00 48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 48 B9 00 00 00 00 00 00 00 00 48 BA 00 00 00 00 00 00 00 00 4C 8D 44 24 08 4C 8D 4D 10 48 B8 F0 AC AB 14 FE 7F 00 00 FF D0 48 81 C4 90 01 00 00 48 8B E5 5D C3";

  vector<BYTE> code_bytes = byteStr2Bytes(code_str);

  memcpy_s(code_bytes.data() + 0x21, sizeof(uintptr_t), &lpCB, sizeof(uintptr_t));
  memcpy_s(code_bytes.data() + 0x2B, sizeof(uintptr_t), &lpContext, sizeof(uintptr_t));
  memcpy_s(code_bytes.data() + 0x3E, sizeof(uintptr_t), &lpMethod, sizeof(uintptr_t));

  auto newmem = VirtualAlloc(0, code_bytes.size() + sizeof(uintptr_t), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  memcpy_s(newmem, code_bytes.size(), code_bytes.data(), code_bytes.size());
  return newmem;
}

BOOL cb_test(Local<Function> &cb, Local<Context> &context, uintptr_t *a, uintptr_t *b)
{
  // Will not print, and will give an error
  if (cb->IsFunction())
  {
    printf("xxx\n");
  }
  return FALSE;
};

void callback(const FunctionCallbackInfo<Value> &args)
{
  Isolate *isolate = args.GetIsolate();
  auto context = isolate->GetCurrentContext();

  auto cb = args[0].As<Function>();
  LPVOID addr = createCallbackFunc(&cb_test, &cb, &context);

  args.GetReturnValue().Set(Number::New(isolate, (uintptr_t)addr));
}

我现在的环境是windows10和nodejs 14.15.4

您的示例不完整(例如,createCallbackFunc 做了什么?)因此很难确定发生了什么,但这里有几点需要考虑:

  • 只要对象可访问,它们就不会被垃圾回收。这就是垃圾收集器的作用。
  • 您不能 free/reclaim 手动处理由垃圾收集器管理的对象。
  • 我怀疑在手头的案例中,问题是句柄。顾名思义,Local<...> 意味着在明确定义的本地范围内使用。它的生命周期与周围环境 HandleScope 的生命周期相关。如果 createCallbackFunc 试图将它存放在某个地方,那么预计 Local 很快就会失效(这并没有说明它最初指向的对象,也与垃圾无关集电极)。您可以在 https://v8.dev/docs/embed.
  • 了解更多关于句柄及其生命周期的信息
  • 如果您想要的是一个弱引用和一个在对象被垃圾回收时调用的回调,请查看“weak persistents”。请注意,无法保证何时(或是否会)调用此类回调(这只是“尽力而为”,但例如关闭时可能会被跳过),因此将它们用于关键资源管理不是一个好主意.

更新问题后编辑: createCallbackFunc 的来源揭示了两个问题:

  1. lpCB 是指向堆栈分配对象的指针,memcpy_s(..., &lpCB, ..) 您正在创建 地址 的副本这个指针。那行不通;首先是因为 lpCB 本身是非常短暂的,其次是因为它引用的堆栈分配的 cb (在函数 callback 中)同样是短暂的。因此,以这种方式创建的任何指针都会在 callback 函数 returns 后立即指向随机垃圾。 (这部分问题是标准 C++,与 V8 或 Node 或 GC 或 Windows 或任何其他细节无关。)

  2. 正如我在之前的回答中所猜测的那样,您实际上是在尝试创建 Local 的长期副本。使用 Persistent 来完成它。请阅读文档。

总结:不要在 v8::Local 上使用 memcpy