【问题标题】:How to handle V8 engine crash when process runs out of memory进程内存不足时如何处理 V8 引擎崩溃
【发布时间】:2013-05-28 16:51:45
【问题描述】:

node 控制台和 Qt5 基于 V8 的QJSEngine 都可以通过以下代码崩溃:

a = []; for (;;) { a.push("hello"); }

节点崩溃前的输出:

FATAL ERROR: JS Allocation failed - process out of memory

QJSEngine崩溃前的输出:

#
# Fatal error in JS
# Allocation failed - process out of memory
#

如果我在调试器下运行 QJSEngine 测试应用程序(见下文),它会在 V8 代码中显示 v8::internal::OS::DebugBreak 调用。如果我将调用 QJSEngine::evaluate 的代码包装到 __try-__except (SEH) 中,那么应用程序不会崩溃,但此解决方案是特定于 Windows 的。

问题:有没有办法在节点和 Qt 应用程序中以独立于平台的方式处理 v8::internal::OS::DebugBreak

=== QJSEngine 测试代码 ===

开发环境:QtCreator 与 Qt5 和 Windows SDK 7.1,在 Windows XP SP3 上

QJSEngineTest.pro:

TEMPLATE = app
QT -= gui
QT += core qml
CONFIG -= app_bundle
CONFIG += console
SOURCES += main.cpp
TARGET = QJSEngineTest

没有 SEH 的 main.cpp(这会崩溃):

#include <QtQml/QJSEngine>

int main(int, char**)
{
  try {
    QJSEngine engine;
    QJSValue value = engine.evaluate("a = []; for (;;) { a.push('hello'); }");
    qDebug(value.isError() ? "Error" : value.toString().toStdString().c_str());
  } catch (...) {
    qDebug("Exception");
  }
  return 0;
}

带有 SEH 的 main.cpp(这不会崩溃,输出“致命异常”):

#include <QtQml/QJSEngine>
#include <Windows.h>

void runTest()
{
  try {
    QJSEngine engine;
    QJSValue value = engine.evaluate("a = []; for (;;) { a.push('hello'); }");
    qDebug(value.isError() ? "Error" : value.toString().toStdString().c_str());
  } catch (...) {
    qDebug("Exception");
  }
}

int main(int, char**)
{
  __try {
    runTest();
  } __except(EXCEPTION_EXECUTE_HANDLER) {
    qDebug("Fatal exception");
  }
  return 0;
}

【问题讨论】:

    标签: node.js v8 qt5 seh debugbreak


    【解决方案1】:

    我不相信有跨平台的方法来捕获 V8 致命错误,但即使有,或者如果有某种方法可以在您关心的所有平台上捕获它们,我也不确定那是什么会买你的。

    问题在于 V8 使用了一个global flag 来记录是否发生了致命错误。一旦设置了该标志,V8 将拒绝任何创建新 JavaScript 上下文的尝试,因此无论如何继续都没有意义。在捕获最初的致命错误后尝试执行一些良性 JavaScript 代码。如果我是对的,你马上就会遇到另一个致命错误。

    在我看来,正确的做法是让 Node 和 Qt 将 V8 配置为一开始就不会引发致命错误。现在 V8 支持隔离和内存限制,进程终止的致命错误不再适用。不幸的是,V8 的错误处理代码似乎还没有完全支持这些新功能,并且仍然假设内存不足的情况总是不可恢复的。

    【讨论】:

    • 还请注意,您可以在中止之前使用 V8::SetFatalErrorHandler() 注册一个回调,尽管这仍然无法让您展开 v8 堆栈。您还可以使用 --no-hard_abort 控制 OS::Abort() 以使进程安静地退出 (SIGABRT) 而不是访问冲突 (V8_IMMEDIATE_CRASH())。 (我不确定在什么情况下会调用 DebugBreak() - 可能仅在附加调试器时。)
    猜你喜欢
    • 2015-04-10
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    相关资源
    最近更新 更多