Re: Analyzing Memory Usage
I did a whole lot more studying of this and learning the tools. I created a simpler scenario and used the .NET Object Allocation tool that is available in 2022. It's pretty nice, though somewhat a work in progress, too. Specifically, I created a simpler scenario that created a bunch of objects, and should have discarded them. I then added a GC.Collect line which would do two things. The first thing it would do would be to give me an easily recognizable point in the allocation tracking. A hard Collect call like that stands out like a flag (a red flag, to be precise) in the Object delta graph. The second thing it would do for me would be to collect everything that could be collected.
The tool allows you to see the results of any collection. It shows a pie chart of the top collected items and a pie chart of the top survived types. Half of the survived types were strings, which didn't surprise me any. In fact, one thing I found was that the top two or three memory hogs were Fake classes created for testing that did nothing other than logging what was called. That was nice, because it means that those can largely be ignored. They aren't representative of the model in actual use.
The model that was being tested is essentially a series of one or more nodes connected by spokes, which is a common pattern that shows up in a lot of programming problems. Whenever a new spoke is going to be added, the model clones itself, the new spoke is added to the clone, the model is validated, and if it passes, the original is replaced by the clone. This design has an obvious issue. Each new spoke requires that a clone be made of every existing spoke. I added 100 spokes, so the first time...there was nothing to clone, so that was one spoke. The second time a spoke was added, the existing spoke was cloned and a new spoke was added. The third time, then the two existing spokes were cloned and a new spoke was added, and so forth for 100 spokes.
Therefore, a LOT of spokes got created in this test, but at the end, only 100 spokes should have been reachable. I was surprised to see that spokes made it into the Top Survived Types pie chart. In fact, spokes make up over 25% of the survived types. There are a couple slight deficiencies in this chart, though, because it's showing the top 5 object types, but it doesn't tell me what percentage of the total types those five make up. There are 44,333 objects that survived the GC.Collect, so if it was JUST those 5, that would mean somewhere over 11,000 spokes survived, but only about 5000 should have ever been created. However, since I don't know what those top 5 represent, that number could be anything.
It would be nice if I could see how many objects those are, and perhaps I can, but I'm not understanding what I'm seeing. If I click on one of the slices in the pie chart, I see allocations, and there are 100 spokes, the exact number that I feel should have survived, but if I sort the allocations by number or size, those spokes aren't in the top five. In fact, they are 13th on the list, so I'm not quite sure what that list holds. It isn't objects that survived the collection, because there are over 44K of those, and the list holds maybe 30K. Not sure what to make of that.
Another deficiency of the pie chart is that, since it only shows the top 5 for recovered and survived, I don't see a means to see how many spokes got recovered. I can select a window to look at, and see that almost the exact number of spokes are created that I would expect (I expected 5,050, but see 5,150), and there's that list that suggests that only 100 remain, but the list that suggests that only 100 remain shows up only if I click the pie slice for spokes remaining. I can't get to that list by any other means that I have found, so I'm not quite sure what that list means. In fact, I can't always get to the list by clicking on the pie chart. It seems to depend on what window of time I have selected on the Object Delta graph.
There are also items on that list that I get to by clicking the pie chart that don't look like objects at all. The largest single allocation item is an ArrayListEnumeratorSimple, which is fine, as I suppose that would be an object, but there is also GetOneAU, which is a method in the model, not what I would expect to be an object.
There's a lot of information, but I can't say I fully understand it, yet.