Skip to content

Stop the menu-bar icon leak (about 28 KB/s while idle) - #16

Merged
MichalAFerber merged 2 commits into
mainfrom
fix/tray-icon-leak
Sep 14, 2026
Merged

MichalAFerber merged 2 commits into
mainfrom
fix/tray-icon-leak

Conversation

@tgwab-claude

@tgwab-claude tgwab-claude commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #15.

What leaked. Tomatick 2.0.0 grew about 28 KB/s on the Mac while idle, the rate that reaches 10.34 GB in about four and a half days.

  • heap showed the menu-bar icon's decode chain added once per second: NSData, CFData, CGImage, and the ImageIO objects. That was +602 of each in 10 minutes and +1,806 in 30.
  • @autoreleasepool storage grew in step with the chains, from 57 to 172 pages over the same windows.
  • updateTitle re-set the unchanged idle icon on every one-second tick. Each call makes systray build a new NSImage from the PNG bytes, and the decoded copies were never released.
  • The measurements are in the [Michal] looks like maybe we have a memory bug. #15 comments.

Change

  1. withAutoreleasePool (macOS only) wraps the timer-driven callbacks in repeat, so Cocoa objects autoreleased during tray updates are released. That covers the one-second tick and the 90 ms alarm animation.
    • fyne runs fyne.Do callbacks outside any autorelease pool.
    • The helper calls the Objective-C runtime's objc_autoreleasePoolPush and objc_autoreleasePoolPop directly, because this package builds with -fobjc-arc (dock_darwin.go), where NSAutoreleasePool is unavailable.
    • On other platforms it just calls fn.
  2. showIcon sets the tray icon only when the resource changes. setupTray, updateTitle, and onAnim all go through it.

Measured on the Mac (macOS 26.6.2): each build was relaunched idle with the red theme, then sampled for 10 minutes with footprint and heap.

build footprint over 10 min malloc heap growth icon decode chains added
2.0.0 (main) 36 → 53 MB +16.7 MB +602
pool only (first commit) 33 → 36 MB +0.1 MB 0
pool + showIcon (this PR) 33 → 33 MB +0.08 MB 0

Pool-only also held @autoreleasepool content at 7 pages; on 2.0.0 it grew from 57 to 172. With showIcon added, the pool-only build's remaining per-tick string growth (+346 CFString in 10 minutes) is gone as well.

Not verified

  • A running timer, where the title changes every second, and the alarm animation. Both run through the same pooled callback, but neither could be started remotely.
  • Linux and Windows: only showIcon changes behavior there. CI builds all three platforms.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KX5fU4VDLe5MbyaytQJByw

tgwab-claude and others added 2 commits September 14, 2026 17:05
fyne runs fyne.Do callbacks outside any autorelease pool, so Cocoa objects
autoreleased while the tray is updated from the one-second and 90 ms tickers
were never released. Tomatick 2.0.0 grew about 28 KB/s while idle (#15).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX5fU4VDLe5MbyaytQJByw
updateTitle re-set the unchanged idle icon every second, and each call makes
systray build and decode a new NSImage. showIcon skips the call when the
resource already showing is the one requested (#15).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX5fU4VDLe5MbyaytQJByw
@MichalAFerber
MichalAFerber enabled auto-merge (squash) September 14, 2026 21:27
@MichalAFerber
MichalAFerber merged commit 078ea33 into main Sep 14, 2026
6 checks passed
@MichalAFerber
MichalAFerber deleted the fix/tray-icon-leak branch September 14, 2026 21:29
tgwab-claude added a commit that referenced this pull request Sep 23, 2026
2.0.0 (tagged 2026-09-10) predates #16 (merged 2026-09-14), so every
installed copy still leaks about 28 KB/s while idle. Refs #20.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjrZpzfYSjYw3sW4XibcCd
tgwab-claude added a commit that referenced this pull request Sep 23, 2026
* scripts: add a manual regression guard for the tray memory leak

Refs #20. The leak in #20 (Tomatick hit 6.13 GB, macOS warned) is the same
bug as #15: it was already fixed on main by #16 (autorelease pool around
timer-driven tray updates), merged 2026-09-14 -- but the installed
/Applications/Tomatick.app is still built from the v2.0.0 tag (2026-09-10),
four days before that fix landed, so Michal has been running the pre-fix
binary the whole time. No new code leak was found beyond what #16 already
closes; this branch is origin/main plus a guard.

#16 didn't leave a repeatable check behind, so add
scripts/measure-tray-memory.sh: builds the app, runs it idle for 10
minutes, and samples footprint every 30s, with the pass/fail threshold
taken from #16's own before/after numbers (pre-fix ~28 KB/s idle vs
post-fix ~0.08 MB/10 min). It has to run by hand on a Mac GUI session, so
it isn't wired into CI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjrZpzfYSjYw3sW4XibcCd

* Bump to 2.0.1 to ship the tray memory fix (#16)

2.0.0 (tagged 2026-09-10) predates #16 (merged 2026-09-14), so every
installed copy still leaks about 28 KB/s while idle. Refs #20.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjrZpzfYSjYw3sW4XibcCd

---------

Co-authored-by: tgwab-claude <326333458+tgwab-claude@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

[Michal] looks like maybe we have a memory bug.

2 participants