Skip to content

Use far less memory while Syncer sits in the tray - #49

Merged
ApolloF merged 2 commits into
mainfrom
ApolloF/RAM-Opt
Oct 18, 2026
Merged

ApolloF merged 2 commits into
mainfrom
ApolloF/RAM-Opt

Conversation

@ApolloF

@ApolloF ApolloF commented Oct 18, 2026

Copy link
Copy Markdown
Owner

What

Syncer sat at ~420 MB working set in the tray (Syncer.exe + 6 WebView2 processes), almost all of it a window nobody was looking at.

  • Tray memory trim (internal/winx/trim.go, tray.go): 5 s after the window hides to the tray, and 45 s after a --tray start, Syncer runs debug.FreeOSMemory and EmptyWorkingSet on itself and its WebView2 children. Pages move to Windows' standby list and return on demand; nothing is closed or lost.
  • No UI polling while hidden (frontend/src/lib/refresher.ts): the 15 s refresh timer and the changed event refetch pause while hidden and catch up when the window shows (window event, WindowHidden() binding).

No data formats, sync, backup, conflict, API or account code changed.

Measurements (claude-dev, Windows 11, same release build method, wails build; 60 s sample after settling)

Working set / private bytes, WebView2 = 6 processes under Syncer.exe. Syncthing was already running (2 processes on this box, not started or configured by the run), so it is identical both times.

Scenario Metric Before (main) After
Tray (--tray) Syncer.exe WS 98 / 50 MB (2 runs) 9 MB
WebView2 WS 321 / 327 MB 58 MB
Total Syncer-owned WS ~420 / ~378 MB ~67 MB
Private bytes (Syncer + WebView2) ~179 / ~166 MB ~155 MB (private is committed memory; only the working set is released)
CPU in 60 s (Syncer + WebView2) 0.56 / 0.33 s 0.17 s
Window open Syncer.exe WS 50.6 / 54.9 MB 54.9 MB
WebView2 WS 317 / 320 MB 316 MB
CPU in 60 s (Syncer + WebView2) 3.2 / 0.54 s (first run noisy) 0.60 s
Background run (--background, nothing to back up here) peak WS 32 MB 32 MB
Syncthing (2 procs) WS 80 MB 80 MB (untouched)

Handles/threads unchanged (~600 handles, ~30 threads for Syncer; ~2.6k / ~132 for WebView2). The window-open numbers are equal within noise, as expected: the trim only runs while hidden. The background-run scenario had no folders to back up on this machine, so it only proves no regression at startup, not a heavy backup.

Not implemented (proposals)

  • Destroy the WebView2 when closed to the tray: would drop private bytes too (~95 MB), but Wails v2 can't recreate the window cleanly, and reopening would be visibly slower. Needs a decision.
  • WebView2 MemoryUsageTargetLevel = Low while hidden: not reachable through Wails v2's public API.
  • Syncthing settings: Syncer doesn't own the user's global options (usage/crash reporting, rescan intervals), so I left them. A soft GOMEMLIMIT on the child it launches is possible but of little use at 40 MB per process.
  • Remaining background loops (15 s foreground check, 30 s pause/mods checks, 1 min notify) are cheap (~0.1 s CPU/min together); not worth the risk of delaying game-session detection.
  • No Go heap/goroutine pprof was taken: Syncer.exe is only ~30 threads and ~60 MB private, with no pprof endpoint, and I didn't add one just for this.

Verification

go test -short ./... && go test -short -tags personal ./... && npm --prefix frontend run check && npm --prefix frontend test exits 0. UI not visually changed.

The window stops refreshing while hidden, and Syncer and its web view hand
their idle memory back to Windows a few seconds after the window hides.
@ApolloF
ApolloF merged commit 886c674 into main Oct 18, 2026
3 checks passed
@ApolloF
ApolloF deleted the ApolloF/RAM-Opt branch October 18, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant