Skip to content

Memory leak: Room instances remain in the heap after disconnect(), accumulating across repeated join/leave cycles (reproducible on meet.livekit.io) #2008

Description

@OTaran2107

Describe the bug

We've identified what appears to be a memory leak in livekit-client where Room instances remain strongly reachable after disconnect() and are never garbage collected.

The issue is reproducible not only in our production application, but also on the official LiveKit demo.
This suggests the problem is inside the SDK rather than in application code.

Reproduction

Steps to reproduce

  1. Open https://meet.livekit.io/.
  2. Join any room.
  3. Leave the room.
  4. Open Chrome DevTools → Memory.
  5. Click Collect garbage.
  6. Take a Heap Snapshot.
  7. Repeat the process multiple times.
Image

Logs

System Info

livekit-client 2.19.2
Chrome (latest stable)
macOS
Reproduced both in:
our production React application
https://meet.livekit.io

Severity

Severity: High.
Every completed call permanently retains Room instances. The retained object graph grows linearly with the number of completed meetings and is expected to have an increasing memory impact over the lifetime of the page.

Additional Information

Actual result

Room instances remain in the heap after every completed call.
In my test I performed 6 completely independent join/leave cycles.

After each call I:

  • left the room,
  • forced garbage collection,
  • created a new Heap Snapshot.

After the sixth call the heap contained: 12 retained Room instances (exactly 2 additional Room instances per completed call)

Expected result

After await room.disconnect(); and a garbage collection cycle, the Room instance should no longer be reachable.
A completed meeting should not permanently increase the number of retained Room objects.

Impact

The reproduction above was performed using a meeting with a single participant.
Even in this minimal scenario, completed meetings permanently accumulate retained Room instances.
Since a Room owns or references the entire session graph (participants, track publications, media tracks, peer connections, transports, etc.), the impact is expected to grow with meeting complexity.

For larger meetings (for example, 10+ participants), each retained Room is likely to keep references to substantially more objects than in the single-participant case, increasing the amount of memory that cannot be reclaimed after a meeting ends.

Why this is important

Room is not a lightweight object. It is the root of the LiveKit session graph and owns or references objects such as:

  • LocalParticipant
  • RemoteParticipants
  • TrackPublications
  • media tracks
  • RTCEngine
  • PCTransportManager
  • RTCPeerConnections
  • EventEmitters
  • media transports

Retaining Room therefore means retaining a significant portion of the LiveKit session graph.
Over repeated join/leave cycles these retained session graphs accumulate, leading to steadily increasing memory usage during the lifetime of the page.

Suspected root cause

While investigating the SDK we noticed what may be the cause. In the Room constructor a devicechange listener is registered using an internal wrapper function.
Later, during disconnect, a different function reference appears to be passed to removeEventListener().
If this analysis is correct, the listener is never removed and continues to retain the associated closure.
We are not claiming this is definitively the root cause, only that it matches the observed heap-retention behavior.

Evidence

The issue reproduces:
✅ in our production application
✅ in the official LiveKit demo (meet.livekit.io)
✅ after explicit disconnect()
✅ after forced garbage collection
✅ across multiple independent join/leave cycles

The number of retained Room instances increases monotonically and never decreases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions