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

时间:2019-04-02 15:42:38

标签: javascript c++ v8

我正在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。

1 个答案:

答案 0 :(得分:2)

尝试在调用期间使用短暂的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个句柄。