Skip to content

C emission needs 14+ GB on a 16 GB Mac because JSC sizes its heap from physical RAM #1262

Open

Description

@melbaldove

What you did

bend daemon_http.bend -o daemon_http.c

(a ~27k-line program: about 1,800 defs, 24 MB of C)

What happened

On a 16 GB Apple M-series Mac, bend app.bend -o app.c on a ~27k-line program
(about 1,800 defs, 24 MB of C) grows its memory footprint to 14+ GB within
40 seconds. With other work on the machine, the process then compresses and
swaps: one emission ran 94 minutes, reached a 32 GB footprint, and did not
finish. Setting BUN_JSC_forceRAMSize=2147483648 makes the same emission
finish in about 2 minutes at 5–9 GB, with byte-identical C.

Measurements (one emission at a time)

Tree Bend Environment Result Peak footprint C size
A (morning) 2.0.31 default killed at 52 s >14 GB –
A 2.0.34 default killed at 36 s 14.3 GB –
B (+290 defs) 2.0.31 default killed at 37 s 14.3 GB –
B 2.0.31 BUN_JSC_forceRAMSize=4GiB 132 s, ok 9.1 GB 24,060,021
B 2.0.31 BUN_JSC_forceRAMSize=2GiB 124 s, ok 8.6 GB 24,060,021 (identical)
A 2.0.31 forceRAMSize=2GiB 139 s, ok 4.8 GB 22,354,260
B 2.0.34 forceRAMSize=2GiB 126 s, ok 5.8 GB 24,061,798

A larger file in the same program (a test driver that imports every module)
did not finish in 7 minutes on 2.0.31 even with forceRAMSize=2GiB (760 s
system time, memory compression). On 2.0.34 with the same setting it finished
in 385 s.

Observations

  • The emitter allocates roughly 300–400 MB/s of short-lived garbage. JSC sizes
    its heap from physical RAM, so the GC lets the heap grow toward the machine's
    size before it collects.
  • compile_book re-emits all defs until the owner and hot facts reach a
    fixed point. On program B it needs at least 5 passes (facts 0 → 4149 →
    4351 → 4384 → 4389 → …), at about 25 s per pass.
  • The TELES memo (tele_unbind2) is keyed by term identity and never cleared
    between defs; it holds about 890K entries after file_book. Clearing it did
    not reduce peak memory much.

Suggestions

  1. Set a bounded heap for the compiler by default (for example the equivalent
    of BUN_JSC_forceRAMSize), or trigger a collection between emission passes.
  2. Emit only the defs whose facts changed on each fixed-point pass, instead of
    all defs.
  3. Clear per-def memos such as TELES after each def.

Workaround

export BUN_JSC_forceRAMSize=2147483648
bend app.bend -o app.c

The file

Not small (about 27k lines across many modules).

bend version

bend 2.0.34 (also seen on 2.0.31; darwin-arm64 release binaries)

uname -sm

Darwin arm64 (Apple M-series, 16 GB RAM, 10 cores)

clang --version (the first line)

clang version 21.1.8 (not involved: the memory growth happens during bend -o .c, before any C compile)

Activity

  1. Lorenzobattistela commented on Oct 14, 2026

    @Lorenzobattistela
    Collaborator

    Thanks for the measurements. We built a synthetic program of the same size:
    27.6k lines, 2,634 defs, 33 modules, 14.4 MB of C. It is made from copies of
    the repo's demos, each its own module, imported by one entry. On current main
    (dbb3157) -o app.c peaks at 2.2 GB RSS in 6.5 s with 5 fixed-point passes,
    and 2.0.34 behaves about the same on it (2.3 GB, 8.3 s). So we can't
    reproduce the 14 GB without your program. Our passes cost ~1 s; yours ~25 s.
    Some things that went in after 2.0.34 may have fixed it: graph_close became
    linear (#1298), TELES now lives per file and stays small (~4k entries here,
    it grew to 146k on 2.0.34), and #1196/#1244/#1304/#1362. Could you retry
    bend app.bend -o app.c on 2.0.37 and post the peak and time? If it still
    blows up, a reduced program (or the module that dominates) would help most.

    On the heap: BUN_JSC_forceRAMSize still matters. It cuts our peak by 44%
    (2.2 → 1.2 GB) for +6% time with identical C. A Bun.gc per pass gives only
    ~10%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions