Skip to content

3.76 GB allocated against 11 MB reachable, growing ~10 MB/min; .sys.gc reclaims nothing #602

Description

@vbmithr

Summary

A long-lived Rayforce service reports 3.76 GB allocated against 11.4 MB of
reachable data
, growing ~10 MB/min indefinitely. .sys.gc reclaims 32 KB of
it. A second service on the same build, same IPC subscription path and a
larger working set is clean, so this is not simply how the allocator behaves.

I could not reduce it to a primitive. What follows is the evidence and
everything I ruled out, in case it is enough for you to recognise.

The process

An incremental consumer: subscribes to a multicast topic, buffers arriving
rows in a global table, and every 5 s runs a pass of group-bys, joins and
anti-joins over what arrived, appending results to a findings table that is
trimmed to a 100k-row cap.

(.sys.mem)
  bytes-allocated : 3,764,364,736     (3.76 GB)
  peak-bytes      : 3,787,658,624
  alloc-count     : 69,584,761
  sys-current     : 4,330,172,416
  anon-committed  : 4,328,521,728

(.mem.objsize findings) :  5,374,456   (5.4 MB,  94k rows)
(.mem.objsize depth)    :  5,967,396   (6.0 MB,  ~10k rows, oscillating)
(.mem.objsize last_msg) :     38,374   (38 KB,   774 rows)
(.sys.env)              :        407   globals

11.4 MB reachable through globals; 3.76 GB allocated. RSS 4.1 GB. Uptime 7 h.

It is retained, not uncollected

before (.sys.gc) : 3,766,785,344
after  (.sys.gc) : 3,766,752,576      (32,768 bytes reclaimed)

It grows linearly, while the working set does not

Sampled 60 s apart:

bytes-allocated alloc-count findings rows depth rows
3,763,437,824 69,648,121 94,280 11,355
3,772,720,320 69,819,874 94,451 7,944
3,783,627,264 70,004,906 94,769 9,501

~10 MB/min, ~3,000 allocations/s, with both tables flat. That rate over the
uptime accounts for the whole 4.1 GB.

A control on the same build

Another service of ours, same Rayforce, also a multicast subscriber, with a
much larger working set (534k-row table), 98 min uptime:

bytes-allocated : 124 MB
(.mem.objsize levels) : 44 MB
(.mem.objsize output) : 50 MB

124 MB against 94 MB visible. No gap, no growth. So the IPC/subscribe path and
table mutation in general are not implicated.

Ruled out

Each run in a loop against tables of the shape the service uses (50k rows,
773 distinct symbol keys), with .sys.gc before and after, measuring
bytes-allocated delta. All reported 0 MB:

  • anti-join on a two-column key, 200 reps
  • inner-join on a two-column key, 200 reps
  • select {by: ...} group-by, 200 reps
  • select {where: ...}, 200 reps
  • xasc, 100 reps
  • concat, 200 reps
  • insert into a global then trim with a negative take, 300 reps
  • reassigning a global from an anti-join, 300 reps
  • delete {from: 'global where: ...}, 300 reps
  • format with 3 and 7 arguments, 20,000 reps
  • map over til, 20,000 reps
  • at on a column, 20,000 reps

So the leak is not in those operations standalone. What the real service adds
over the probes is that it runs them from a timer callback, on data arriving
through a multicast subscription
, continuously for hours -- rather than in a
tight loop in one evaluation.

What would help

If there is a counter or a debug build that attributes live allocations by
call site, I am happy to run it against this process and report back -- it is
reproducible simply by leaving the service up. Equally, if alloc-count minus
frees is tracked anywhere, that delta would localise it quickly.


Measured on rayforce 2.6.2.r229.ge7770412.

Activity

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