dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.27k stars 5.9k forks source link

Large Object Heap docs missing details #38558

Open zivkan opened 11 months ago

zivkan commented 11 months ago

Type of issue

Missing information

Description

Unless you're reasonably knowledgeable about the .NET CLR Memory performance counters, I don't think it's obvious that the counter category is ".NET CLR Memory", so I wasn't really sure what to look for in Performance Monitor when I first opened it up. The docs even say:

Use "Add Counters" to add the interesting counter for processes that you care about.

I think this could be clearer. The counter category and name should be called out explicitly, not implicitly via heading titles.

Figure 4, which shows a screenshot of Performance Monitor is FAR too small. When I take a screenshot of the docs page and open in Paint, the text on the page is 16 pixels high, but the text in the screenshot is only 5 pixels high. And that's from top of the capital C to the bottom of the lower case p, in the word Computer. So the lower case o letter is literally 3 pixels tall. It's just not readable, especially since the text is anti-aliased, making it just look blurry when zoomed in, rather than a 1990's low resolution screen.

The end of this section says that "Performance counters can also be queried programmatically". However, the lack of clarity earlier that ".NET CLR Memory" is the categoryName and that "Large Object Heap size" is the counterName, makes it slower to try to get started.

However, there's a much more difficult problem to solve, which isn't explained in these docs. When trying to use it in a test app, I get an exception "Counter is not single instance, an instance name needs to be specified". So, I tried setting counter.InstanceName = Process.GetCurrentProcess().ProcessName, but then I get an exception "Instance 'ConsoleApp2' does not exist in the specified Category". Similarly, when I make my console app wait via Console.ReadLine(), and check Windows' Performance Monitor while the app is running, I can't find my app in the instance list.

Therefore, I'm blocked from being able to take this doc's advice for querying the performance counter programmatically, because the docs doesn't explain how, or have to link to another doc, explaining how to get the app listed in the counter's instance list. All my quick searches just find Stack Overflow questions where the .NET Framework counter isn't appearing for any process, and needs to be re-registered with Windows. I haven't yet found how to make my specific app appear in the list of instances.

both screen shots are much too small. Neither are clickable to view a larger version of the image. I tried right clicking the images and using the browser's "open image in new tab", which shows that indeed the images are larger and more readable, although the first screenshot (figure 5) is still quite small. The text is significantly smaller than the text on the docs page itself. Anyway, if it's intended for customers reading these docs to need to look at the image in a new tab, I don't think it's a safe assumption that everyone knows about "right click, open in new tab". The image itself should be clickable to take us to the full size image, similar to wikipedia.

I also have a question about this section more generally. It says this:

collects an AllocationTick event, which is fired approximately every 100k worth of allocations. In other words, an event is fired each time a large object is allocated.

Is a Gen2 GC with "LargeAlloc" as the reason guaranteed every LOH allocation? Even if I alloc a byte array that is the minimum size for LOH? 85k is less than 100k, but the docs explicitly say that "fired approximately every 100k worth of allocations", which would suggest that not all LOH allocations will trigger a GC.

Plus earlier on it says:

As you can see, all GCs are generation 2 GCs, and they are all triggered by AllocLarge, which means that allocating a large object triggered this GC. We know that these allocations are temporary because the LOH Survival Rate % column says 1%.

If every single LOH allocation triggered a GC, then shouldn't survival rate generally be greater than 50%? For it to be 1% in 24 consecutive Gen2 garbage collections, that means over 50 or 100 large objects need to have been allocated since the last Gen2 GC, only 1% of them survive (and PerfView rounds the percentage down). However, since a LOH alloc needs to be 85k or higher, 50 of these would be 4250kB, which is much higher than the "approximately 100k worth of allocations".

Page URL

https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap

Content source URL

https://github.com/dotnet/docs/blob/main/docs/standard/garbage-collection/large-object-heap.md

Document Version Independent Id

ed840139-0331-77b1-5645-0cac6c16edcf

Article author

@gewarren

Metadata


Associated WorkItem - 206393

gewarren commented 8 months ago

Thanks for the feedback @zivkan. I've addressed most of it in https://github.com/dotnet/docs/pull/39683.

Regarding finding the instance name for the counters, I feel that is not really specific to garbage collection, so I'm not planning to address it in my PR (and I don't know the answer).

@Maoni0 may be able to address this question:

Is a Gen2 GC with "LargeAlloc" as the reason guaranteed every LOH allocation? Even if I alloc a byte array that is the minimum size for LOH? 85k is less than 100k, but the docs explicitly say that "fired approximately every 100k worth of allocations", which would suggest that not all LOH allocations will trigger a GC.

Plus earlier on it says:

As you can see, all GCs are generation 2 GCs, and they are all triggered by AllocLarge, which means that allocating a large object triggered this GC. We know that these allocations are temporary because the LOH Survival Rate % column says 1%.

If every single LOH allocation triggered a GC, then shouldn't survival rate generally be greater than 50%? For it to be 1% in 24 consecutive Gen2 garbage collections, that means over 50 or 100 large objects need to have been allocated since the last Gen2 GC, only 1% of them survive (and PerfView rounds the percentage down). However, since a LOH alloc needs to be 85k or higher, 50 of these would be 4250kB, which is much higher than the "approximately 100k worth of allocations".