GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — src: add percentage support to --max-old-space-size · nodejs/node@686ac49

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • doc
    • api
      • cli.md
    • node-config-schema.json
    • node.1
  • src
    • node.cc
    • node_options.cc
    • node_options.h
  • test/parallel
    • test-max-old-space-size-percentage.js
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+17Lines changed: 17 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,22 @@ changes:
17021702

17031703
Specify the maximum size, in bytes, of HTTP headers. Defaults to 16 KiB.
17041704

1705+
### `--max-old-space-size-percentage=PERCENTAGE`
1706+
1707+
Sets the max memory size of V8's old memory section as a percentage of available system memory.
1708+
This flag takes precedence over `--max-old-space-size` when both are specified.
1709+
1710+
The `PERCENTAGE` parameter must be a number greater than 0 and up to 100. representing the percentage
1711+
of available system memory to allocate to the V8 heap.
1712+
1713+
```bash
1714+
# Using 50% of available system memory
1715+
node --max-old-space-size-percentage=50 index.js
1716+
1717+
# Using 75% of available system memory
1718+
node --max-old-space-size-percentage=75 index.js
1719+
```
1720+
17051721
### `--napi-modules`
17061722

17071723
<!-- YAML
@@ -3387,6 +3403,7 @@ one is included in the list below.
33873403
* `--inspect`
33883404
* `--localstorage-file`
33893405
* `--max-http-header-size`
3406+
* `--max-old-space-size-percentage`
33903407
* `--napi-modules`
33913408
* `--network-family-autoselection-attempt-timeout`
33923409
* `--no-addons`
Collapse file

‎doc/node-config-schema.json‎

Copy file name to clipboardExpand all lines: doc/node-config-schema.json
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@
278278
"max-http-header-size": {
279279
"type": "number"
280280
},
281+
"max-old-space-size-percentage": {
282+
"type": "string"
283+
},
281284
"network-family-autoselection": {
282285
"type": "boolean"
283286
},
Collapse file

‎doc/node.1‎

Copy file name to clipboardExpand all lines: doc/node.1
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,16 @@ The file used to store localStorage data.
346346
.It Fl -max-http-header-size Ns = Ns Ar size
347347
Specify the maximum size of HTTP headers in bytes. Defaults to 16 KiB.
348348
.
349+
.It Fl -max-old-space-size-percentage Ns = Ns Ar percentage
350+
Sets the max memory size of V8's old memory section as a percentage of available system memory.
351+
This flag takes precedence over
352+
.Fl -max-old-space-size
353+
when both are specified.
354+
The
355+
.Ar percentage
356+
parameter must be a number greater than 0 and up to 100, representing the percentage
357+
of available system memory to allocate to the V8 heap.
358+
.
349359
.It Fl -napi-modules
350360
This option is a no-op.
351361
It is kept for compatibility.
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,13 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
818818
// anymore.
819819
v8_args.emplace_back("--no-harmony-import-assertions");
820820

821+
if (!per_process::cli_options->per_isolate->max_old_space_size_percentage
822+
.empty()) {
823+
v8_args.emplace_back(
824+
"--max_old_space_size=" +
825+
per_process::cli_options->per_isolate->max_old_space_size);
826+
}
827+
821828
auto env_opts = per_process::cli_options->per_isolate->per_env;
822829
if (std::find(v8_args.begin(), v8_args.end(),
823830
"--abort-on-uncaught-exception") != v8_args.end() ||
Collapse file

‎src/node_options.cc‎

Copy file name to clipboardExpand all lines: src/node_options.cc
+48Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
#include "node_external_reference.h"
88
#include "node_internals.h"
99
#include "node_sea.h"
10+
#include "uv.h"
1011
#if HAVE_OPENSSL
1112
#include "openssl/opensslv.h"
1213
#endif
1314

1415
#include <algorithm>
1516
#include <array>
1617
#include <charconv>
18+
#include <cstdint>
1719
#include <limits>
1820
#include <sstream>
1921
#include <string_view>
@@ -103,8 +105,49 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors,
103105
per_isolate->CheckOptions(errors, argv);
104106
}
105107

108+
void PerIsolateOptions::HandleMaxOldSpaceSizePercentage(
109+
std::vector<std::string>* errors,
110+
std::string* max_old_space_size_percentage) {
111+
std::string original_input_for_error = *max_old_space_size_percentage;
112+
// Parse the percentage value
113+
char* end_ptr;
114+
double percentage =
115+
std::strtod(max_old_space_size_percentage->c_str(), &end_ptr);
116+
117+
// Validate the percentage value
118+
if (*end_ptr != '\0' || percentage <= 0.0 || percentage > 100.0) {
119+
errors->push_back("--max-old-space-size-percentage must be greater "
120+
"than 0 and up to 100. Got: " +
121+
original_input_for_error);
122+
return;
123+
}
124+
125+
// Get available memory in bytes
126+
uint64_t total_memory = uv_get_total_memory();
127+
uint64_t constrained_memory = uv_get_constrained_memory();
128+
129+
// Use constrained memory if available, otherwise use total memory
130+
// This logic correctly handles the documented guarantees.
131+
// Use uint64_t for the result to prevent data loss on 32-bit systems.
132+
uint64_t available_memory =
133+
(constrained_memory > 0 && constrained_memory != UINT64_MAX)
134+
? constrained_memory
135+
: total_memory;
136+
137+
// Convert to MB and calculate the percentage
138+
uint64_t memory_mb = available_memory / (1024 * 1024);
139+
uint64_t calculated_mb = static_cast<size_t>(memory_mb * percentage / 100.0);
140+
141+
// Convert back to string
142+
max_old_space_size = std::to_string(calculated_mb);
143+
}
144+
106145
void PerIsolateOptions::CheckOptions(std::vector<std::string>* errors,
107146
std::vector<std::string>* argv) {
147+
if (!max_old_space_size_percentage.empty()) {
148+
HandleMaxOldSpaceSizePercentage(errors, &max_old_space_size_percentage);
149+
}
150+
108151
per_env->CheckOptions(errors, argv);
109152
}
110153

@@ -987,6 +1030,11 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
9871030
V8Option{},
9881031
kAllowedInEnvvar);
9891032
AddOption("--max-old-space-size", "", V8Option{}, kAllowedInEnvvar);
1033+
AddOption("--max-old-space-size-percentage",
1034+
"set V8's max old space size as a percentage of available memory "
1035+
"(e.g., '50%'). Takes precedence over --max-old-space-size.",
1036+
&PerIsolateOptions::max_old_space_size_percentage,
1037+
kAllowedInEnvvar);
9901038
AddOption("--max-semi-space-size", "", V8Option{}, kAllowedInEnvvar);
9911039
AddOption("--perf-basic-prof", "", V8Option{}, kAllowedInEnvvar);
9921040
AddOption(
Collapse file

‎src/node_options.h‎

Copy file name to clipboardExpand all lines: src/node_options.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,17 @@ class PerIsolateOptions : public Options {
283283
bool report_uncaught_exception = false;
284284
bool report_on_signal = false;
285285
bool experimental_shadow_realm = false;
286+
std::string max_old_space_size_percentage;
287+
std::string max_old_space_size;
286288
int64_t stack_trace_limit = 10;
287289
std::string report_signal = "SIGUSR2";
288290
bool build_snapshot = false;
289291
std::string build_snapshot_config;
290292
inline EnvironmentOptions* get_per_env_options();
291293
void CheckOptions(std::vector<std::string>* errors,
292294
std::vector<std::string>* argv) override;
295+
void HandleMaxOldSpaceSizePercentage(std::vector<std::string>* errors,
296+
std::string* max_old_space_size);
293297

294298
inline std::shared_ptr<PerIsolateOptions> Clone() const;
295299

Collapse file

‎test/parallel/test-max-old-space-size-percentage.js‎

Copy file name to clipboard
+134Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
'use strict';
2+
3+
// This test validates the --max-old-space-size-percentage flag functionality
4+
5+
require('../common');
6+
const assert = require('node:assert');
7+
const { spawnSync } = require('child_process');
8+
const os = require('os');
9+
10+
// Valid cases
11+
const validPercentages = [
12+
'1', '10', '25', '50', '75', '99', '100', '25.5',
13+
];
14+
15+
// Invalid cases
16+
const invalidPercentages = [
17+
['', /--max-old-space-size-percentage= requires an argument/],
18+
['0', /--max-old-space-size-percentage must be greater than 0 and up to 100\. Got: 0/],
19+
['101', /--max-old-space-size-percentage must be greater than 0 and up to 100\. Got: 101/],
20+
['-1', /--max-old-space-size-percentage must be greater than 0 and up to 100\. Got: -1/],
21+
['abc', /--max-old-space-size-percentage must be greater than 0 and up to 100\. Got: abc/],
22+
['1%', /--max-old-space-size-percentage must be greater than 0 and up to 100\. Got: 1%/],
23+
];
24+
25+
// Test valid cases
26+
validPercentages.forEach((input) => {
27+
const result = spawnSync(process.execPath, [
28+
`--max-old-space-size-percentage=${input}`,
29+
], { stdio: ['pipe', 'pipe', 'pipe'] });
30+
assert.strictEqual(result.status, 0, `Expected exit code 0 for valid input ${input}`);
31+
assert.strictEqual(result.stderr.toString(), '', `Expected empty stderr for valid input ${input}`);
32+
});
33+
34+
// Test invalid cases
35+
invalidPercentages.forEach((input) => {
36+
const result = spawnSync(process.execPath, [
37+
`--max-old-space-size-percentage=${input[0]}`,
38+
], { stdio: ['pipe', 'pipe', 'pipe'] });
39+
assert.notStrictEqual(result.status, 0, `Expected non-zero exit for invalid input ${input[0]}`);
40+
assert(input[1].test(result.stderr.toString()), `Unexpected error message for invalid input ${input[0]}`);
41+
});
42+
43+
// Test NODE_OPTIONS with valid percentages
44+
validPercentages.forEach((input) => {
45+
const result = spawnSync(process.execPath, [], {
46+
stdio: ['pipe', 'pipe', 'pipe'],
47+
env: { ...process.env, NODE_OPTIONS: `--max-old-space-size-percentage=${input}` }
48+
});
49+
assert.strictEqual(result.status, 0, `NODE_OPTIONS: Expected exit code 0 for valid input ${input}`);
50+
assert.strictEqual(result.stderr.toString(), '', `NODE_OPTIONS: Expected empty stderr for valid input ${input}`);
51+
});
52+
53+
// Test NODE_OPTIONS with invalid percentages
54+
invalidPercentages.forEach((input) => {
55+
const result = spawnSync(process.execPath, [], {
56+
stdio: ['pipe', 'pipe', 'pipe'],
57+
env: { ...process.env, NODE_OPTIONS: `--max-old-space-size-percentage=${input[0]}` }
58+
});
59+
assert.notStrictEqual(result.status, 0, `NODE_OPTIONS: Expected non-zero exit for invalid input ${input[0]}`);
60+
assert(input[1].test(result.stderr.toString()), `NODE_OPTIONS: Unexpected error message for invalid input ${input[0]}`);
61+
});
62+
63+
// Test percentage calculation validation
64+
function getHeapSizeForPercentage(percentage) {
65+
const result = spawnSync(process.execPath, [
66+
'--max-old-space-size=3000', // This value should be ignored, since percentage takes precedence
67+
`--max-old-space-size-percentage=${percentage}`,
68+
'--max-old-space-size=1000', // This value should be ignored, since percentage take precedence
69+
'-e', `
70+
const v8 = require('v8');
71+
const stats = v8.getHeapStatistics();
72+
const heapSizeLimitMB = Math.floor(stats.heap_size_limit / 1024 / 1024);
73+
console.log(heapSizeLimitMB);
74+
`,
75+
], {
76+
stdio: ['pipe', 'pipe', 'pipe'],
77+
env: {
78+
...process.env,
79+
NODE_OPTIONS: `--max-old-space-size=2000` // This value should be ignored, since percentage takes precedence
80+
}
81+
});
82+
83+
if (result.status !== 0) {
84+
throw new Error(`Failed to get heap size for ${percentage}: ${result.stderr.toString()}`);
85+
}
86+
87+
return parseInt(result.stdout.toString(), 10);
88+
}
89+
90+
const testPercentages = [25, 50, 75, 100];
91+
const heapSizes = {};
92+
93+
// Get heap sizes for all test percentages
94+
testPercentages.forEach((percentage) => {
95+
heapSizes[percentage] = getHeapSizeForPercentage(percentage);
96+
});
97+
98+
// Test relative relationships between percentages
99+
// 50% should be roughly half of 100%
100+
const ratio50to100 = heapSizes[50] / heapSizes[100];
101+
assert(
102+
ratio50to100 >= 0.4 && ratio50to100 <= 0.6,
103+
`50% heap size should be roughly half of 100% (got ${ratio50to100.toFixed(2)}, expected ~0.5)`
104+
);
105+
106+
// 25% should be roughly quarter of 100%
107+
const ratio25to100 = heapSizes[25] / heapSizes[100];
108+
assert(
109+
ratio25to100 >= 0.15 && ratio25to100 <= 0.35,
110+
`25% heap size should be roughly quarter of 100% (got ${ratio25to100.toFixed(2)}, expected ~0.25)`
111+
);
112+
113+
// 75% should be roughly three-quarters of 100%
114+
const ratio75to100 = heapSizes[75] / heapSizes[100];
115+
assert(
116+
ratio75to100 >= 0.65 && ratio75to100 <= 0.85,
117+
`75% heap size should be roughly three-quarters of 100% (got ${ratio75to100.toFixed(2)}, expected ~0.75)`
118+
);
119+
120+
// Validate heap sizes against system memory
121+
const totalMemoryMB = Math.floor(os.totalmem() / 1024 / 1024);
122+
const margin = 10; // 5% margin
123+
testPercentages.forEach((percentage) => {
124+
const upperLimit = totalMemoryMB * ((percentage + margin) / 100);
125+
assert(
126+
heapSizes[percentage] <= upperLimit,
127+
`Heap size for ${percentage}% (${heapSizes[percentage]} MB) should not exceed upper limit (${upperLimit} MB)`
128+
);
129+
const lowerLimit = totalMemoryMB * ((percentage - margin) / 100);
130+
assert(
131+
heapSizes[percentage] >= lowerLimit,
132+
`Heap size for ${percentage}% (${heapSizes[percentage]} MB) should not be less than lower limit (${lowerLimit} MB)`
133+
);
134+
});

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.