IOAccelerator GPU memory leak: idle sessions accumulate ~1 GB non-reclaimable footprint each

Resolved 💬 7 comments Opened Mar 18, 2026 by alexey-pelykh Closed Apr 16, 2026

Summary

Long-lived Claude Code sessions accumulate 700–968 MB of non-reclaimable IOAccelerator (GPU) dirty pages that are never freed. This is invisible to RSS monitoring (7 MB RSS vs 1.3 GB footprint — 15× underestimate) but triggers macOS "out of memory" warnings attributed to the parent terminal. On a 32 GB machine with 37 idle sessions, this totals ~41 GB of memory pressure from GPU buffers alone.

Root Cause

Claude Code uses Ink (React for terminals) on Bun (JSC/WebKit). The rendering pipeline goes through WebKit's GPU compositor, which allocates IOAccelerator 128 MB buffer slabs. These slabs grow monotonically during the session lifetime and are never freed or reused.

Ink React render → Yoga layout → JSC/WebKit compositor → IOAccelerator GPU buffers (128 MB slabs, never freed)

This is confirmed to be Claude Code-specific (Ink rendering), not Bun baseline. Plain Bun processes stay at ~1.3 MB IOAccelerator. Claude Code sessions grow to 10 slabs (966 MB) over days.

Reproduction

  1. Start Claude Code: claude
  2. Use it actively for 1+ hours
  3. Check: footprint -p $(pgrep -f 'claude' | head -1)
  4. Observe IOAccelerator growing in 128 MB increments
  5. Leave idle — IOAccelerator dominates footprint, never shrinks

Control

echo "await Bun.sleep(3600000);" | bun run -
# footprint shows IOAccelerator stays at ~1.3 MB

Evidence

Footprint breakdown — idle session (PID 29561, 15 days old)

Footprint: 1324 MB   (RSS: 7 MB)

 968 MB    IOAccelerator      ← 73% of footprint, GPU dirty pages
 265 MB    WebKit malloc      ← JS heap
  37 MB    JS VM Gigacage
  24 MB    MALLOC_SMALL
  18 MB    JS JIT generated code

IOAccelerator slab structure (vmmap)

54db4000000-54dbc000000  128.0M  swap: 95.7M   ← oldest, nearly full
54dbc000000-54dc4000000  128.0M  swap: 95.3M
54dc4000000-54dcc000000  128.0M  swap: 126.3M
54dcc000000-54dd4000000  128.0M  swap: 99.7M
54dd4000000-54ddc000000  128.0M  swap: 127.5M
54ddc000000-54de4000000  128.0M  swap: 127.5M
54de4000000-54dec000000  128.0M  swap: 122.9M
54dec000000-54df4000000  128.0M  swap: 106.1M
54df4000000-54dfc000000  128.0M  swap: 65.0M
54dfc000000-54e04000000  128.0M  swap: 80K     ← newest
(reserved)                768.0M                ← pre-allocated for future slabs

10 × 128 MB slabs, all dirty, non-reclaimable. Peak footprint was 2.8 GB.

Growth by session age

| Session State | IOAccel Slabs | IOAccel Dirty | Total Footprint | RSS |
|---------------|--------------|---------------|-----------------|-----|
| Fresh bun (no Ink) | 1 | 1.3 MB | 6.5 MB | 5 MB |
| Claude Code (3 hours) | 4 | 46 MB | 443 MB | 393 MB |
| Claude Code (8 days idle) | ~8 | 711 MB | 996 MB | 8 MB |
| Claude Code (15 days idle) | 10 | 966 MB | 1324 MB | 7 MB |

System-wide impact (37 sessions on 32 GB machine)

  • Total footprint: 62.7 GB (vs 4.1 GB RSS)
  • macOS memory pressure: 41% free
  • Compressed pages: 14.6 GB
  • macOS showed "iTerm2 using 64 GB" (all claude descendants attributed to parent)

Additional: leaks tool detected 175,613 leaked objects

Process 29561: 175613 leaks for 13551072 total leaked bytes.

(Standard malloc zone only; WebKit malloc zone unreadable due to security restrictions.)

Environment

  • OS: macOS 26.3 (25D125), Apple Silicon
  • Claude Code: 2.1.78
  • Runtime: Bun (JSC/WebKit), confirmed via binary strings and memory categories
  • Terminal: iTerm2 3.6.6
  • RAM: 32 GB

Measurement Commands

# Real footprint (what macOS uses for memory warnings)
footprint -p <pid>

# IOAccelerator slab details
vmmap -summary <pid> | grep IOAccel

# System memory pressure
sysctl -n kern.memorystatus_level

# Compare: RSS understates by 15×
ps -o pid,rss -p <pid>   # 7 MB (lies)
footprint -p <pid>        # 1324 MB (truth)

Suggested Mitigations

  1. Periodic GPU buffer release: Investigate if IOAccelerator slabs can be explicitly freed when the session is idle
  2. Compositor opt-out: Explore whether Bun's JSC/WebKit compositor can be disabled for terminal rendering (Ink does not need GPU compositing)
  3. Session recycling: Long-idle sessions could be offered for cleanup/restart
  4. Documentation: Note that footprint, not RSS, reflects actual memory cost on macOS

Related Issues

  • #24113 — IOAccelerator observed at 2.3 GB, but retracted (attributed to Ghostty)
  • #33453 — WebKit Malloc growth (different category, same runtime)
  • #18859 — 15 GB per idle session (same symptom, root cause not identified)
  • #33589 — ArrayBuffer accumulation (different root cause)
  • oven-sh/bun#21560 — RSS grows while JS heap flat (GPU cause not identified)

Full deep-dive analysis with raw data and reproduction methodology is available upon request.

View original on GitHub ↗

This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗