Browse Source
AvaloniaAccessHelper keeps every automation peer it has ever handed to Android in three dictionaries (_peers, _peerIds, _peerNodeInfoProviders) and never removes any of them - the file contains no Remove or Clear call. Each ControlAutomationPeer holds a strong reference to its Owner, so once accessibility has explored a control, that control (and the visual tree hanging off it) is pinned for the lifetime of the AvaloniaView. This is invisible in an app with a static UI, but it is unbounded in one that rebuilds its visual tree: on a digital-signage device rebuilding its screen every 20-40 s, this retained one full dead screen per rebuild - about 2.5 MB of live managed heap each time, measured after a forced gen2 collection, growing until the low-memory killer stepped in. Removing the stale entries brought the same bench from unbounded growth to a flat plateau over 28 consecutive rebuilds. The registration is now dropped when the peer's control is detached from the visual tree, and the two peer event handlers are unsubscribed at the same time (they were never removed either). Virtual view IDs are now allocated from a monotonic counter instead of being derived from _peerNodeInfoProviders.Count. That was safe only while nothing was ever removed: with removal, Count can fall back onto an ID that is still in use and the next registration would throw from Dictionary.Add inside an accessibility callback. The root peer (ID 0) is deliberately left registered: GetVirtualViewAt and GetVisibleVirtualViews index _peers[0] directly, so removing it would throw. It is a single entry, owned by the view, and dies with the helper.pull/22069/head
committed by
GitHub
1 changed files with 41 additions and 4 deletions
Loading…
Reference in new issue