Problem
QtWebEngineProcess (and any embedded Chromium) accumulates Oilpan/Blink detached DOM
nodes during agent sessions with rapid tool-call rounds, even with the 2.5s post-response
gc() call in place.
Root cause: _gcPending lockout in rapid agent loops
The 5000ms lockout window means that when 4 rapid tool responses complete in succession,
only the first gets a GC cycle. Responses 2–4 accumulate Oilpan garbage with no
collection:
T=0s Response 1 finishes → gc() dispatched → _gcPending = true
T=2s Response 2 finishes → gc() BLOCKED (_gcPending)
T=4s Response 3 finishes → gc() BLOCKED
T=6s Response 4 finishes → gc() BLOCKED
T=5s _gcPending = false ← no catch-up fires; 3 responses' garbage stranded
There is no mechanism to detect that GC was blocked or to collect the missed cycles.
Secondary: element-node in-place patch uses innerHTML unnecessarily
streamingRenderer.js in-place patch uses _tailNodes[i].innerHTML = newNodes[i].innerHTML
for element-node updates, which destroys and recreates child nodes even when both old and
new have a single text child. The simpler firstChild.data = assignment suffices and
avoids one detach+create cycle per token for plain-text elements.
Solution
A: Missed-GC catch-up flag — reduce lockout 5000ms → 3000ms; add _gcMissed flag;
when GC completes and _gcMissed is true, immediately dispatch one catch-up GC cycle and
log [GC] catch-up dispatched. A run of N rapid responses gets at most 2 GC cycles
(primary + catch-up) rather than 1.
B: Element-node text-data patch — for element nodes where both live and new have a
single text-node child, use firstChild.data = instead of innerHTML =. Adds _rtSaved
counter logged in finalize() alongside the existing _rtCalls/_rtFast metrics.
Context
This primarily affects embedded Chromium (QtWebEngine, Electron) where OS memory pressure
signals are not wired to Oilpan's coordinator, so Oilpan never spontaneously runs a major
collection. gc({ type: 'major', execution: 'async' }) via --expose-gc is the only
reliable mechanism — making it fire reliably between agent rounds is critical for
long-running sessions.
[GC] blocked — catch-up queued and [GC] catch-up dispatched log lines route through
javaScriptConsoleMessage into wrapper_system.log for diagnostic visibility.
Problem
QtWebEngineProcess(and any embedded Chromium) accumulates Oilpan/Blink detached DOMnodes during agent sessions with rapid tool-call rounds, even with the 2.5s post-response
gc()call in place.Root cause:
_gcPendinglockout in rapid agent loopsThe 5000ms lockout window means that when 4 rapid tool responses complete in succession,
only the first gets a GC cycle. Responses 2–4 accumulate Oilpan garbage with no
collection:
There is no mechanism to detect that GC was blocked or to collect the missed cycles.
Secondary: element-node in-place patch uses
innerHTMLunnecessarilystreamingRenderer.jsin-place patch uses_tailNodes[i].innerHTML = newNodes[i].innerHTMLfor element-node updates, which destroys and recreates child nodes even when both old and
new have a single text child. The simpler
firstChild.data =assignment suffices andavoids one detach+create cycle per token for plain-text elements.
Solution
A: Missed-GC catch-up flag — reduce lockout 5000ms → 3000ms; add
_gcMissedflag;when GC completes and
_gcMissedis true, immediately dispatch one catch-up GC cycle andlog
[GC] catch-up dispatched. A run of N rapid responses gets at most 2 GC cycles(primary + catch-up) rather than 1.
B: Element-node text-data patch — for element nodes where both live and new have a
single text-node child, use
firstChild.data =instead ofinnerHTML =. Adds_rtSavedcounter logged in
finalize()alongside the existing_rtCalls/_rtFastmetrics.Context
This primarily affects embedded Chromium (QtWebEngine, Electron) where OS memory pressure
signals are not wired to Oilpan's coordinator, so Oilpan never spontaneously runs a major
collection.
gc({ type: 'major', execution: 'async' })via--expose-gcis the onlyreliable mechanism — making it fire reliably between agent rounds is critical for
long-running sessions.
[GC] blocked — catch-up queuedand[GC] catch-up dispatchedlog lines route throughjavaScriptConsoleMessageintowrapper_system.logfor diagnostic visibility.