Skip to content

fix(cli): configure v8 isolate with cgroups-constrained memory limit #29078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

magurotuna
Copy link
Member

@magurotuna magurotuna commented Apr 28, 2025

This change configures V8 isolates to respect memory limits imposed by cgroups on Linux.

It adds support for detecting both cgroups v1 and v2 memory limits, enabling Deno to properly adapt to containerized environments with memory constraints. When cgroups information is unavailable or not applicable, it falls back to using the system's total memory as before.

Closes #29077

Test

For testing, I created a ubuntu VM with 1Gi memory. Within this VM, set up a cgroup with 512Mi memory limit, then ran the following script to see how much heap size limit the V8 isolate had.

import * as v8 from "node:v8";

console.log(v8.getHeapStatistics());

Ubuntu 20.04

In this version of ubuntu, hybrid mode is enabled by default.

$ cat /proc/self/cgroup
12:rdma:/
11:blkio:/user.slice
10:devices:/user.slice
9:cpu,cpuacct:/user.slice
8:pids:/user.slice/user-1000.slice/session-3.scope
7:memory:/user.slice/user-1000.slice/session-3.scope
6:perf_event:/
5:freezer:/
4:net_cls,net_prio:/
3:hugetlb:/
2:cpuset:/
1:name=systemd:/user.slice/user-1000.slice/session-3.scope
0::/user.slice/user-1000.slice/session-3.scope

Create a new cgroup with 512Mi memory limit and run the above script in this cgroup:

$ sudo cgcreate -g memory:/mygroup
$ sudo cgset -r memory.limit_in_bytes=$((512 * 1024 * 1024)) mygroup
$ sudo cgexec -g memory:mygroup ./deno run main.mjs
{
  total_heap_size: 7745536,
  total_heap_size_executable: 0,
  total_physical_size: 7090176,
  total_available_size: 266348216,
  used_heap_size: 6276752,
  heap_size_limit: 271581184,
  malloced_memory: 303200,
  peak_malloced_memory: 140456,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  number_of_detached_contexts: 0,
  total_global_handles_size: 24576,
  used_global_handles_size: 22432,
  external_memory: 3232012
}

This indicates that the isolate was informed of cgroup-constrained memory limit (512Mi) and hence got ~270M heap limit.

Ubuntu 22.04

In this version of ubuntu, cgroup v2 is used.

$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-3.scope

Run the above script using systemd-run:

$ sudo systemd-run --property=MemoryMax=512M --pty bash -c '/home/ubuntu/deno run /home/ubuntu/main.mjs'
{
  total_heap_size: 7745536,
  total_heap_size_executable: 0,
  total_physical_size: 7090176,
  total_available_size: 266348184,
  used_heap_size: 6276784,
  heap_size_limit: 271581184,
  malloced_memory: 303200,
  peak_malloced_memory: 140456,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  number_of_detached_contexts: 0,
  total_global_handles_size: 24576,
  used_global_handles_size: 22432,
  external_memory: 3232012
}

Again the isolate got ~270M heap limit properly.
Note that it should have had bigger heap limit if the entire system memory, i.e. 1Gi, had been passed to V8. In fact, if we run the same script outside the cgroup, it does display larger heap_size_limit like below:

$ ./deno run main.mjs
{
  total_heap_size: 7745536,
  total_heap_size_executable: 0,
  total_physical_size: 7090176,
  total_available_size: 546580152,
  used_heap_size: 6276752,
  heap_size_limit: 551813120,
  malloced_memory: 303200,
  peak_malloced_memory: 140456,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  number_of_detached_contexts: 0,
  total_global_handles_size: 24576,
  used_global_handles_size: 22432,
  external_memory: 3232012
}

This change configures V8 isolates to respect memory limits imposed by
cgroups on Linux. It adds support for detecting both cgroups v1 and v2
memory limits, enabling Deno to properly adapt to containerized
environments with memory constraints. When cgroups information is
unavailable or not applicable, it falls back to using the system's
total memory as before.
@magurotuna magurotuna force-pushed the tell-v8-cgroup-constrained-memory-limit branch from 8ed9d12 to 895d941 Compare April 29, 2025 07:48
@magurotuna magurotuna marked this pull request as ready for review April 30, 2025 08:54
@magurotuna magurotuna requested a review from Copilot April 30, 2025 08:54
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR configures V8 isolates to honor memory limits imposed by cgroups on Linux by detecting cgroup v1 and v2 configurations. Key changes include:

  • Adding a new Linux-specific logic branch in create_isolate_create_params to use cgroup memory limits.
  • Introducing a helper module with functions to parse /proc/self/cgroup and determine the correct memory limit.
  • Including tests to validate the cgroup parsing function for both v1 and v2 (and hybrid mode).

magurotuna and others added 3 commits April 30, 2025 23:37
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Yusuke Tanaka <[email protected]>
Copy link
Member

@devsnek devsnek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally seems good, I am curious though if there's some crate for parsing the cgroups instead of manually implementing it.

Copy link
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any impact on startup perf? Can we this be feature flagged at compile/runtime instead?

@bartlomieju
Copy link
Member

Is there any impact on startup perf? Can we this be feature flagged at compile/runtime instead?

That's a good point, it won't be used in 99% cases - maybe enable it only via env var

@magurotuna
Copy link
Member Author

I am curious though if there's some crate for parsing the cgroups instead of manually implementing it.

Looks like this crate https://docs.rs/cgroups-rs/0.3.4/cgroups_rs/index.html provides what we want. I'll replace the manual implementation with it.

maybe enable it only via env var

OK, let's do that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cgroups memory limit is not taken into account when configuring V8 heap
4 participants