Performance Issue
Summary of findings
Why 220 MB?
Rough breakdown:
.NET runtime/JIT/:assemblies:assemblies~50-80MB(baseline, unavoidable)Garbage Collectionpressure from.ToList()in hot-path timers:~30-50MBfragmentation.EF Core / DB connectionpooling:~10-20MB.Framework libraries &:buffers:buffers~40-60MBApplication data (players, UI, caches, track data):~5-10MB
The .NET runtime itself accounts for ~100MB+ baseline. The rest is largely pressure from allocation patterns.GCGarbage Collection
Top Recommendations
Remove .ToList() from timer hot paths (biggest win)
Your MillisecondTimer runs every 50ms and calls .ToList() on players, views, and buttons each iteration. That's thousands of throwaway list allocations per minute.
Files:
Timer/MillisecondTimer.cs(lines54,103,103,110).Timer/SecondTimer.cs(lines110,907,1044+).
// BEFORE - allocates a new list every 50ms
foreach (var conn in _storage.Players.Values.ToList())
// AFTER - iterates in-place, zero allocation
foreach (var conn in _storage.Players.Values)ConcurrentDictionary.Values is safe to enumerate even if modified during iteration - it provides a snapshot enumerator. If you're worried about collection-modified exceptions on the inner Views/Buttons lists, consider using a lock or switching
them to concurrent collections instead of .ToList() on every tick.
Expected savings: 20-50 MB reduction in GC pressure/fragmentation.
2. Audit all .ToList() across the codebase
The explore found .ToList() heavily used in InterfaceManager.Menu.cs, UserAction.cs, and other files. Many of these may be unnecessary. Each one allocates a copy of the entire collection.
3. Fix unbounded PendingOnTrack dictionary
In InSim/Packet/JoinTrack.cs line 27, entries are added but never cleaned up. Over weeks of uptime this slowly leaks. Add a cleanup mechanism similar to NewConnectionInfo.
4. Audit User.TunnelHeight list
In Model/User.cs line 196, List<double> TunnelHeight grows while TunnelHeightActive is true with no cap. If a player stays in a tunnel area for a long time, this list grows unbounded. Add a max size or clear it periodically.
Found 12 instances of new Random() scattered across the codebase (e.g., LocationManager.cs line ). Each creates an unnecessary object. Use the static 286)286Random.Shared instead:
// BEFORE
var jobRange = new Random().Next(5, 10);// AFTER
var jobRange = Random.Shared.Next(5, 10);6. Consider trimming the GC on idle
You can add this to Program.cs to let .NET return memory to the OS more aggressively:
GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;Or periodically call GC.Collect(2, GCCollectionMode.Optimized) during low-activity periods.
What's NOT a problem
-
Player cleanupon disconnect is correct (ConnectionLeave.csremoves from dictionary).-DbContextusage with IDbContextFactory and await using is proper.-Track datain memory (~300KB) is negligible.-ModCacheis small.
The single biggest win is removing .ToList() from the timer loops. That alone should noticeably reduce memory usage and GC pressure. Want me to make any of these changes?