Search code examples
node.jsmemoryv8

What is the default value of available memory when `--max-old-space-size` flag is not used?


Node: 12.16.2

Try to figure out why node application which is running in docker container with memory limit 512mb fails with JavaScript heap out of memory on 256mb and if increase limit to 1500mb than fail with approximately 700mb. Looks like there is only 50% of space is given to old generation objects but I can't find any documentation of this behaviour.

Would be correct to set the old space size to 70% or so of total available memory (would remaining space be enough for other v8 memory sections)?

Error log

<--- Last few GCs --->

[1:0x565508ac6740]    26332 ms: Mark-sweep 255.3 (257.9) -> 254.3 (257.3) MB, 85.7 / 0.0 ms  (+ 65.2 ms in 24 steps since start of marking, biggest step 6.6 ms, walltime since start of marking 162 ms) (average mu = 0.239, current mu = 0.259) allocation fa[1:0x565508ac6740]    26447 ms: Mark-sweep 255.6 (258.1) -> 253.6 (256.3) MB, 112.6 / 0.0 ms  (average mu = 0.159, current mu = 0.021) allocation failure scavenge might not succeed


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x565505bea959]
    1: StubFrame [pc: 0x565505beb82d]
Security context: 0x229e3f6008d1 <JSObject>
    2: /* anonymous */(aka /* anonymous */) [0x278bb3a93ad9] [/app/server.js:~1] [pc=0x180e9c58e6b7](this=0x3479357404b1 <undefined>,0x02f1a1a468a9 <String[30]:  defer="defer" charset="utf-8">,0x347935743389 <String[#9]: anonymous>,0x14f217f67ed9 <String[#11]: crossorigin>)
    3: /* anonymous */(aka /* anonymous */) [0x...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Node report

{
  "header": {
    "reportVersion": 2,
    "event": "Allocation failed - JavaScript heap out of memory",
    "trigger": "FatalError",
    "filename": "report.20200929.110012.1.0.001.json",
    "dumpEventTime": "2020-09-29T11:00:12Z",
    "dumpEventTimeStamp": "1601377212975",
    "processId": 1,
    "threadId": null,
    "cwd": "/app",
    "commandLine": [
      "node",
      "--max-http-header-size=80000",
      "--inspect=0.0.0.0",
      "/app/server.js"
    ],
    "nodejsVersion": "v12.16.2",
    "wordSize": 64,
    "arch": "x64",
    "platform": "linux",
    "componentVersions": {
      "node": "12.16.2",
      "v8": "7.8.279.23-node.34",
      "uv": "1.34.2",
      "zlib": "1.2.11",
      "brotli": "1.0.7",
      "ares": "1.15.0",
      "modules": "72",
      "nghttp2": "1.40.0",
      "napi": "5",
      "llhttp": "2.0.4",
      "http_parser": "2.9.3",
      "openssl": "1.1.1e",
      "cldr": "36.0",
      "icu": "65.1",
      "tz": "2019c",
      "unicode": "12.1"
    },
    "release": {
      "name": "node",
      "lts": "Erbium",
      "headersUrl": "https://unofficial-builds.nodejs.org/download/release/v12.16.2/node-v12.16.2-headers.tar.gz",
      "sourceUrl": "https://unofficial-builds.nodejs.org/download/release/v12.16.2/node-v12.16.2.tar.gz"
    },
    "osName": "Linux",
    "osRelease": "4.19.76-linuxkit",
    "osVersion": "#1 SMP Tue May 26 11:42:35 UTC 2020",
    "osMachine": "x86_64",
    "cpus": [
      {
        "model": "Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz",
        "speed": 2208,
        "user": 7626100,
        "nice": 0,
        "sys": 3549100,
        "idle": 3029580300,
        "irq": 0
      },
      {
        "model": "Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz",
        "speed": 2208,
        "user": 7502400,
        "nice": 0,
        "sys": 3600100,
        "idle": 3030348500,
        "irq": 0
      }
    ],
    "networkInterfaces": [
      {
        "name": "lo",
        "internal": true,
        "mac": "00:00:00:00:00:00",
        "address": "127.0.0.1",
        "netmask": "255.0.0.0",
        "family": "IPv4"
      },
      {
        "name": "eth0",
        "internal": false,
        "mac": "02:42:ac:11:00:02",
        "address": "172.17.0.2",
        "netmask": "255.255.0.0",
        "family": "IPv4"
      }
    ],
    "host": "1fcf963ff82d"
  },
  "javascriptStack": {
    "message": "No stack.",
    "stack": [
      "Unavailable."
    ]
  },
  "nativeStack": [
  ],
  "javascriptHeap": {
    "totalMemory": 269869056,
    "totalCommittedMemory": 268494816,
    "usedMemory": 265315384,
    "availableMemory": 3780432,
    "memoryLimit": 271581184,
    "heapSpaces": {
      "read_only_space": {
        "memorySize": 262144,
        "committedMemory": 33088,
        "capacity": 32808,
        "used": 32808,
        "available": 0
      },
      "new_space": {
        "memorySize": 2097152,
        "committedMemory": 1043808,
        "capacity": 1047456,
        "used": 26608,
        "available": 1020848
      },
      "old_space": {
        "memorySize": 150872064,
        "committedMemory": 150868648,
        "capacity": 149710672,
        "used": 149710672,
        "available": 0
      },
      "code_space": {
        "memorySize": 2002944,
        "committedMemory": 1914816,
        "capacity": 1783680,
        "used": 1783680,
        "available": 0
      },
      "map_space": {
        "memorySize": 2625536,
        "committedMemory": 2625240,
        "capacity": 2217840,
        "used": 2217840,
        "available": 0
      },
      "large_object_space": {
        "memorySize": 111386624,
        "committedMemory": 111386624,
        "capacity": 110999232,
        "used": 110999232,
        "available": 0
      },
      "code_large_object_space": {
        "memorySize": 622592,
        "committedMemory": 622592,
        "capacity": 544544,
        "used": 544544,
        "available": 0
      },
      "new_large_object_space": {
        "memorySize": 0,
        "committedMemory": 0,
        "capacity": 1047456,
        "used": 0,
        "available": 1047456
      }
    }
  },
  "resourceUsage": {
    "userCpuSeconds": 14.3448,
    "kernelCpuSeconds": 0.727153,
    "cpuConsumptionPercent": 57.9692,
    "maxRss": 369463296,
    "pageFaults": {
      "IORequired": 35,
      "IONotRequired": 154703
    },
    "fsActivity": {
      "reads": 10272,
      "writes": 2976
    }
  },
  "uvthreadResourceUsage": {
    "userCpuSeconds": 9.15498,
    "kernelCpuSeconds": 0.423264,
    "cpuConsumptionPercent": 36.8394,
    "fsActivity": {
      "reads": 9192,
      "writes": 2976
    }
  },
  "libuv": [
  ],
  "workers": [
  ],
  "environmentVariables": {
    "STATIC_PREFIX": "http://localhost:4000/static/",
    "NODE_VERSION": "12.16.2",
    "HOSTNAME": "1fcf963ff82d",
    "YARN_VERSION": "1.22.4",
    "SHLVL": "1",
    "HOME": "/root",
    "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "DEV_STATIC": "true",
    "PWD": "/app",
    "NODE_ENV": "production"
  },
  "userLimits": {
    "core_file_size_blocks": {
      "soft": 0,
      "hard": "unlimited"
    },
    "data_seg_size_kbytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "file_size_blocks": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_locked_memory_bytes": {
      "soft": 83968000,
      "hard": 83968000
    },
    "max_memory_size_kbytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "open_files": {
      "soft": 1048576,
      "hard": 1048576
    },
    "stack_size_bytes": {
      "soft": 8388608,
      "hard": "unlimited"
    },
    "cpu_time_seconds": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_user_processes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "virtual_memory_kbytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    }
  },
  "sharedObjects": [
    "/usr/local/bin/node",
    "/usr/lib/libstdc++.so.6",
    "/usr/lib/libgcc_s.so.1",
    "/lib/ld-musl-x86_64.so.1"
  ]
}

Solution

  • What is the default value of available memory when --max-old-space-size flag is not used?

    V8's computation of default memory limits is fairly complicated (and changes every now and then to account for a variety of situations and use cases), you can check out the source code in Heap::ConfigureHeap in heap.cc. Your guess is correct that one of the factors taken into account is that V8 memory should not exceed half of overall available memory. This is mostly geared towards the browser use case; on a server nothing is stopping you from using command-line flags to tune the behavior to your specific needs. Also, Node could override V8's default behavior if it chose to.

    Would be correct to set the old space size to 70% or so of total available memory (would remaining space be enough for other v8 memory sections)?

    Yes. See also Node.js recommended "max-old-space-size".