Description
We run a long-lived kiosk, which suffice sequential 1:1 sessions on a single page (the page is not reloaded between sessions). For each session, we create a client, initialize, join, start audio/video, and at session end we detach everything, call leave(), and await ZoomVideo.destroyClient().
The renderer never returns to its pre-session memory footprint. Every completed session leaves ~70–80 MB of JS heap behind in SDK-created contexts. Over time, memory accumulates linearly and is never reclaimed by idle time (observed for 2-3 hours atleast) or forced GC.
We have two primary questions:
- What is the supported way to release it at session end?
destroyClient()does not clean it up, and we haven’t found a public API that does. There are 2 things left post call end one is ‘call’ which is around 22 MB or more and ‘vb_worker’ which is around 48-50 MB. - Why is a worker named
vb_workercreated per client when Virtual Background is never used? No VB option is passed tostartVideo, no background image is set, and VB APIs aren’t called anywhere in our code. Yet a TF.js worker namedvb_worker(~50 MB, containing a selfie-segmentation model) is spawned on the first join of every client, with a new one accumulating for every session.
Browser Console Error
There is no error, and no warning during teardown—destroyClient() resolves successfully without logging anything.
Which Web Video SDK version?
2.4.0 (We also statically inspected the 2.5.0 bundle; the relevant teardown logic appears identical).
Video SDK Code Snippets
The trimmed-down per-session lifecycle (SDK calls only):
import ZoomVideo from '@zoom/videosdk'
// --- 1. Setup ---
const client = ZoomVideo.createClient()
ZoomVideo.preloadDependentAssets()
await client.init('en-US', 'CDN', { stayAwake: true, leaveOnPageUnload: true })
await client.join(topic, signature, userName)
const stream = client.getMediaStream()
await stream.startAudio()
await stream.startVideo({ hd: true })
await stream.attachVideo(remoteUserId, VideoQuality.Video_720P)
// --- 2. Teardown ---
const players = await stream.detachVideo(remoteUserId)
players.forEach((el) => el.remove())
await stream.stopVideo()
await stream.stopAudio()
await client.leave()
await ZoomVideo.destroyClient()
// The renderer stays on this page, and the next session repeats this block.
Screenshots
Device
-
Device: Desktop PC (x64)
-
OS: Windows 11
-
Browser Version: Chromium
140.0.7339.133
Additional context
Questions we are hoping to get answered:
-
Looking at the cleanup lifecycle: after
destroyClient()runs, do you think doing a full reload is the safest route to guarantee all resources and singletons are fully wiped, or is there a preferred pattern you usually like to follow for post-destroy states? -
Is
vb_workerexpected to boot on the WebRTC video path even when Virtual Background features are never requested? -
Is there a public or supported API method to release these worker/worklet contexts during session teardown?
-
Is there an account or session flag to disable WebRTC video redirection if it isn’t required for our implementation?
-
Would enabling
patchJsMedia: trueallow a remote media-layer patch for this if one becomes available?
