为什么 运行 嵌入在 c++ 中的 v8 中非常简单的脚本会耗尽内存?
Why does running a very simple script in v8, embedded in c++, use up memory?
我正在 运行在 V8 中嵌入一个非常简单的 javascript 脚本,嵌入到 C++ 程序中。该脚本只是一个数字文字:'12'。但是,当我在循环中多次 运行 这个脚本时,我的应用程序将在 20-30 秒后消耗几千兆字节的内存。我正在使用 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(谢谢!)提供的解决方案解决了我的问题。
我将 运行s 100.000.000 次的内部循环分成两个循环;一个具有 100.000 次迭代的外循环和一个具有 1000 次迭代的内循环。在此内部循环之前,我执行 v8::HandleScope temp_scope(isolate);
,现在不再有极端的内存使用情况。该应用程序现在使用 2.3MB。
每个脚本的总时间 运行 也没有增加。相反,它从 85ns 略微减少到 82ns。
尝试在您的调用周围使用短暂的 HandleScope
s:
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
creation/destruction 具有(小的)性能成本,但经常重新创建它们会使更多内存符合垃圾收集条件(并且也会略微加快垃圾收集本身)。根据经验,我的目标是每个 HandleScope
.
大约 1,000 到 1,000,000 个句柄
我正在 运行在 V8 中嵌入一个非常简单的 javascript 脚本,嵌入到 C++ 程序中。该脚本只是一个数字文字:'12'。但是,当我在循环中多次 运行 这个脚本时,我的应用程序将在 20-30 秒后消耗几千兆字节的内存。我正在使用 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(谢谢!)提供的解决方案解决了我的问题。
我将 运行s 100.000.000 次的内部循环分成两个循环;一个具有 100.000 次迭代的外循环和一个具有 1000 次迭代的内循环。在此内部循环之前,我执行 v8::HandleScope temp_scope(isolate);
,现在不再有极端的内存使用情况。该应用程序现在使用 2.3MB。
每个脚本的总时间 运行 也没有增加。相反,它从 85ns 略微减少到 82ns。
尝试在您的调用周围使用短暂的 HandleScope
s:
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
creation/destruction 具有(小的)性能成本,但经常重新创建它们会使更多内存符合垃圾收集条件(并且也会略微加快垃圾收集本身)。根据经验,我的目标是每个 HandleScope
.