You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored
Fix memory leak when opening a save or reloading a pack (#84) (#88)
* Fix memory leak when opening a save or reloading a pack (#84)
Two distinct leaks caused memory to grow ~600 MB per save-open and
~250 MB per reload for large packs:
1. LoadProgress leak (~600 MB per open):
TrackerState.LoadProgress() creates a fresh PackageInstance for the
save's (pack, variant) but the old PI was never removed from
ApplicationModel.mPackageInstances or disposed, so its image caches,
Lua interpreter, and definitional TrackerState accumulated
indefinitely. Fix:
- Add PackageInstance.MigrateStateTo() to transfer a state between
PIs without lifecycle events or disposal.
- In ApplicationModel.LoadProgress(), detect when the PI changed,
migrate the state to the new PI, swap mPackageInstances, and
dispose the old PI (which frees its image caches and definitional
state).
2. Reload leak (~250 MB per reload):
PackageLoader.LoadInto() resets the catalogs (Items, Locations, ...)
but left PackageInstance.ImageCache and SourceImageCache intact. Old
ImageReference keys from the reset catalogs kept decoded SKBitmaps
and IImages alive across every Reload. Fix: call FlushImageCaches()
after the catalog resets so stale decoded images are released before
the new load populates them.
3. Static IconUtility caches (secondary, compounding):
sPngCache and sAlphaMasks used ConcurrentDictionary with strong
references to IImage keys, so Avalonia Bitmaps could never be GC'd
even after the PI's ImageCache was cleared. Fix: switch both to
ConditionalWeakTable so entries are evicted automatically when the
IImage key becomes unreachable.
Closes#84
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Dispose Avalonia Bitmap objects in ImageCache on flush, not just clear
pi.ImageCache.Clear() released the cache references but left the Avalonia
Bitmap objects waiting on the finalizer queue, causing unmanaged bitmap
memory to accumulate across reloads until GC eventually ran. Explicitly
disposing each IImage before clearing mirrors the existing SKBitmap
treatment in SourceImageCache and lets GC reclaim the memory promptly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: EmoSaru <emosaru@emosaru.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
0 commit comments