Skip to content

py-spy immediately OOM-killed on Raspberry Pi Zero 2 W (418 MB RAM, aarch64) #826

@puigru

Description

@puigru

Summary

py-spy 0.4.1 is immediately OOM-killed when attaching to a Python process on a Raspberry Pi Zero 2 W (418 MB total RAM, no swap). Even a single py-spy dump command (one-shot, no recording) consumes ~188 MB RSS before producing any output, leaving no room on a system with ~190 MB available.

Reproduction

Environment

  • Device: Raspberry Pi Zero 2 W Rev 1.0
  • Kernel: Linux 6.1.61-v8 #1 SMP PREEMPT aarch64
  • RAM: 418 MB total, ~190 MB available, 0 swap
  • Python: 3.10.8
  • py-spy: 0.4.1 (py_spy-0.4.1-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl, stripped to 4 MB)
  • Target process: nbdkit with Python plugin (19 threads, 122 MB RSS, 195 memory map entries / 17 KB maps file)

Target process memory profile

VmPeak:  1648952 kB
VmSize:  1648952 kB
VmRSS:    122164 kB
VmData:   468536 kB
VmStk:       132 kB
VmExe:       120 kB
VmLib:      5504 kB
Threads:        19

The large VmSize (1.6 GB) is due to a memory-mapped virtual disk (cow overlay), but physical RSS is only 122 MB. The /proc/PID/maps file is 195 lines / 17 KB — entirely unremarkable.

Steps to reproduce

# System has ~190 MB available
$ free -m
              total        used        free      shared  buff/cache   available
Mem:            418         206         150           8          62         184

# Even dropping caches first doesn't help
$ echo 3 | sudo tee /proc/sys/vm/drop_caches

# All three commands below are OOM-killed:

# 1. Record (30s, low rate, raw format, nonblocking)
$ sudo py-spy record -d 10 -r 10 -f raw -o /tmp/profile.txt --pid $PID --nonblocking
Killed

# 2. Record (30s, speedscope format, nonblocking)
$ sudo py-spy record -d 30 -f speedscope -o /tmp/profile.json --pid $PID --nonblocking
Killed

# 3. Even a single one-shot dump
$ sudo py-spy dump --pid $PID --nonblocking
Killed

OOM killer logs

All three invocations show consistent ~188 MB RSS at time of kill:

[13249.172217] py-spy invoked oom-killer: gfp_mask=0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_COMP|__GFP_ZERO), order=0, oom_score_adj=0
[13249.854438] [  46539]     0 46539    50783    47891   421888        0             0 py-spy
[13249.879179] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=py-spy,pid=46539,uid=0
[13249.896146] Out of memory: Killed process 46539 (py-spy) total-vm:203132kB, anon-rss:188684kB, file-rss:0kB, shmem-rss:2880kB, UID:0

[13347.396689] oom-kill: [...] task=py-spy,pid=46910 [...]
[13347.413705] Out of memory: Killed process 46910 (py-spy) total-vm:203132kB, anon-rss:188056kB, file-rss:0kB, shmem-rss:1152kB, UID:0

[13370.378316] oom-kill: [...] task=py-spy,pid=46996 [...]

Analysis

  • py-spy's total-vm is 203 MB and anon-rss is 188 MB — this is consistent across all 3 invocations, suggesting it's a fixed startup cost rather than unbounded growth.
  • The target process has only 195 memory map entries (17 KB maps file) and 122 MB RSS — this is a small, well-behaved process.
  • 188 MB for a profiler is untenable on embedded/IoT devices. For reference, the Raspberry Pi Zero 2 W (512 MB model, ~418 MB available to userspace) is a popular target for Python applications, and the PS3 shipped with 256 MB total.
  • The --nonblocking flag was used across all invocations.
  • py-spy dump (a single snapshot, no recording) also fails, so this is not about sample buffer accumulation — the cost is at startup.

Expected behavior

py-spy should be usable on devices with 512 MB RAM. A single py-spy dump against a small Python process should not require ~188 MB RSS to produce a one-shot stack trace.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions