Search code examples
v8

V8 Isolates - is there a limit to the number of instances? Crashes at 4.7k instances


I am testing the limits of V8, and how many instances it can create, but I encounter the following error. This uses about 18TB of virtual memory, and 3500MB of real memory. I've previously run programs with 120TB of virtual memory, so I don't think the OS virtual memory limit is an issue. Perhaps V8 should be configured differently? I am using V8_COMPRESS_POINTERS. I am wondering if this could be a problem (2^32*4676 = ~20TB).

// stdout
...
Isolate: 4674
Isolate: 4675
Isolate: 4676

# Fatal error in , line 0
# Check failed: ReleasePages(page_allocator_, reinterpret_cast<void*>(region_.begin()), old_size, region_.size()).
#
#FailureMessage Object: 0x7fffe09bae80
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include "libplatform/libplatform.h"
#include "v8.h"

int main(int argc, char *argv[]) {
    std::cout << "Loading V8..." << std::endl;

    // Initialize V8.
    v8::V8::InitializeICUDefaultLocation(argv[0]);
    v8::V8::InitializeExternalStartupData(argv[0]);
    std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
    v8::V8::InitializePlatform(platform.get());
    v8::V8::Initialize();

    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();

    v8::Isolate *isolates[20000];
    for (int i = 0; i < 20000; i++) {
        std::cout << "Isolate: " << i << std::endl;

        v8::Isolate *isolate = v8::Isolate::New(create_params);
        isolates[i] = isolate;
    }

    return 0;
}

Solution

  • It sounds like you may have found the limit ;-)

    To be fair, from the output you've provided it's not clear what exactly is happening, i.e. where the limit is coming from. It could be a V8-unrelated limitation somewhere in your system. V8 does not have a built-in fixed maximum number of Isolates; it'll let you create new Isolates as long as there are sufficient resources to do so.

    It is expected that with pointer compression turned on, each Isolate reserves a 4GB region of virtual memory on startup. It doesn't sound like virtual memory exhaustion is the issue here though, so I'd guess that turning off pointer compression isn't going to help.

    To understand better what's happening, it might prove insightful to get a stack trace for the failing check (with a Debug build).


    EDIT to update:
    I can repro. What's failing is the call to mprotect, called (indirectly) from v8::internal::IsolateAllocator::CommitPagesForIsolate. At that point, cat /proc/$PID/maps | wc -l returns 65531 for the process, and sysctl vm.max_map_count gives me 65530. So as I suspected, it's not a V8 limitation; it's the Linux kernel's default maximum for mapped memory regions that a process is allowed to have. You can raise that limit if you need more. (Though I somewhat question the usefulness of thousands of Isolates in the same process at the same time -- if they are non-empty, you'll soon run into other memory limits: e.g. 4K isolates * 10MB (which is not much for a nontrivial program) is already 40GB of physical memory consumption.)

    Side note: compiling V8 is really easy if you follow the instructions at https://v8.dev/docs/embed; the only detail that needs adjustment is that you need std=c++14 nowadays.