为什么在 v8 中运行嵌入在 c++ 中的非常简单的脚本会占用内存?

Why does running a very simple script in v8, embedded in c++, use up memory?

本文关键字:脚本 内存 简单 非常 运行 c++ 为什么 v8      更新时间:2023-10-16

我在V8中运行一个非常简单的javascript脚本,嵌入在C++程序中。脚本只是一个数字文字:"12"。但是,当我在循环中多次运行此脚本时,我的应用程序将在 20-30 秒后消耗几 GB 的内存。我使用的是V8 7.5.160,它是在Windows 10和Visual Studio 2017下构建的。

我运行这段代码的原因是了解从C++执行 javascript 时的函数调用开销。我正在研究使用 V8 作为应用程序的脚本引擎,该应用程序将以非常高的速率调用许多小脚本。但是内存使用让我担心。

我一直在寻找手动触发垃圾收集器的方法。但据我了解,这应该是不必要的(而且我找不到该怎么做)。 但我更感兴趣的是我是否可以首先阻止这种内存使用。

我使用了 V8 嵌入文档 (hello-world.cc) 中的示例代码,使用不同的脚本,在循环中运行脚本并计时执行调用所需的时间:

// V8 setup code, unchanged from the V8 embedding hello-world.cc.
// My script:
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "12", v8::NewStringType::kNormal).ToLocalChecked();
// Compile script:
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
// Then this code in two nested loops, where the inner loop times the time it takes.
// The outer loop runs 20 times. The inner loop runs 100.000.000 times.
// Inside the inner loop there is only this line of code:
script->Run(context).ToLocalChecked();

我期望这段代码不会导致如此高的内存使用率。或者至少,我希望垃圾回收器能够保持较低的内存使用率。

如果能深入了解导致此脚本内存使用的原因以及防止这种情况发生的方法,我将不胜感激。

已解决

jmrk提供的解决方案(谢谢!)解决了我的问题。 我将运行 100.000.000 次的内部循环分成两个循环;具有 100.000 次迭代的外部循环和具有 1000 次迭代的内部循环。在这个内部循环之前,我确实v8::HandleScope temp_scope(isolate);,现在没有更极端的内存使用。应用程序现在保持在 2.3MB 的使用量。 每次脚本运行的总时间也没有增加。相反,它从85ns略微下降到82ns。

尝试在调用周围使用短暂的HandleScope

for (int i = 0; i < 20; i++) {
/* Record start time */
for (int j = 0; j < 1000; j++) {
v8::HandleScope temp_scope(isolate);
for (int k = 0; k < 100000; k++) {
script->Run(context).ToLocalChecked();
}
}
/* Record end time */
}

背景是.ToLocalChecked()创建了一个新的v8::Local,只要当前HandleScope存在,其内容(=脚本调用的结果)就会保持活动状态。因此,通过在一个HandleScope内为许多新对象创建许多新句柄,您可以有效地禁用垃圾回收。

权衡是HandleScope创建/销毁具有(小)性能成本,但经常重新创建它们会使更多内存符合垃圾回收条件(并且还会稍微加快垃圾回收本身的速度)。根据经验,我的目标是每HandleScope大约 1,000 到 1,000,000 个句柄。