Skip to content

Commit c006800

Browse files
authored
Another1 (#22)
1 parent 3d6a77b commit c006800

File tree

1 file changed

+15
-8
lines changed

1 file changed

+15
-8
lines changed

doc/.NETMemoryPerformanceAnalysis.md

+15-8
Original file line numberDiff line numberDiff line change
@@ -1070,11 +1070,15 @@ The GCStats view has a convenient rollup table at the top of each process that s
10701070

10711071
For gen2 GCs, we’d like to see most or all of them done as BGCs. But if you do see a full blocking GC (denoted as 2N in GCStats), chances are its pause time is long if your heap is big (you will see gen2 GCs have very high Promote MB compared to ephemeral GCs’). Usually what folks do at this point is to take a Heap Snapshot to see what’s on the heap and try to reduce that. However the first thing to figure out is why you are doing full blocking GCs in the first place. You can check the Condemned Reasons table for this. The most common reasons are [high memory load](#GC-is-per-process-but-is-aware-of-physical-memory-load-on-the-machine) and [gen2 fragmentation](#How-often-GCs-are-triggered). To find out the memory load that each GC observed, click on the "Raw Data XML file (for debugging)" link right above the "GC Rollup By Generation" table in the GCStats for that process and it will generate an xml file that includes additional information including memory load. An example is (I trimmed away most of the info) -
10721072

1073-
<GCEvent GCNumber= "45" GCGeneration="2" >
1073+
```c
1074+
<GCEvent GCNumber= "45" GCGeneration="2" >
10741075
<GlobalHeapHistory FinalYoungestDesired="69,835,328" NumHeaps="32"/>
1075-
<PerHeapHistories Count="32" **MemoryLoad="47"**>
1076+
<PerHeapHistories Count="32" MemoryLoad="47">
10761077
</PerHeapHistory>
10771078
</GCEvent>
1079+
```
1080+
1081+
This says when GC#45 happened it observed a memory load of 47%.
10781082

10791083
##### Long pause due to bugs
10801084

@@ -1094,21 +1098,24 @@ If you see ephemeral GCs suddenly promote a lot more, then it’s expected that
10941098

10951099
I’m not aware of any other tools that would tell you this info conveniently (if you know of any tool that tells what old generation objects hold onto young gen objects that make them survive during a GC, please kindly let me know!).
10961100

1097-
Note that if you have an object in gen2/LOH that holds references of young gen objects and if you don’t need them to refer to those objects anymore, you’ll need to manually set those reference fields to null. Otherwise they will [continue to hold those objects live and cause them to be promoted](#1-The-generational-aspect). For C# programs, this is a main reason that causes ephemeral objects to survive (for F# programs, not so much). You can see this from the Raw XML generated by the GCStats view (click on the "Raw Data XML file (for debugging)" link right above the "GC Rollup By Generation" table) and I trimmed most attributes away from the xml:
1101+
Note that if you have an object in gen2/LOH that holds references of young gen objects and if you don’t need them to refer to those objects anymore, you’ll need to manually set those reference fields to null. Otherwise they will [continue to hold those objects live and cause them to be promoted](#1-The-generational-aspect). For C# programs, this is a main reason that causes ephemeral objects to survive (for F# programs, not so much). You can see this from the Raw XML generated by the GCStats view (click on the "Raw Data XML file (for debugging)" link right above the "GC Rollup By Generation" table) and I trimmed most attributes away from the xml -
10981102

1103+
```c
10991104
<GCEvent GCNumber="9" GCGeneration="0">
11001105

11011106
<PerHeapHistories Count="12" MemoryLoad="20">
11021107

1103-
<PerHeapHistory MarkStack ="0.145(10430)" MarkFQ ="0.001(0)"
1108+
<PerHeapHistory MarkStack="0.145(10430)" MarkFQ="0.001(0)"
11041109

1105-
MarkHandles ="0.005(296)" **MarkOldGen** ="2.373(755538)">
1110+
MarkHandles="0.005(296)" MarkOldGen="2.373(755538)">
11061111

1107-
<PerHeapHistory MarkStack ="0.175(14492)" MarkFQ ="0.001(0)"
1112+
<PerHeapHistory MarkStack="0.175(14492)" MarkFQ="0.001(0)"
1113+
1114+
MarkHandles="0.003(72)" MarkOldGen="2.335(518580)">
1115+
```
11081116

1109-
​ MarkHandles ="0.003(72)" **MarkOldGen** ="2.335(518580)">
11101117

1111-
How many bytes promoted by each GC thread due to various kinds of [roots](#What-makes-an-object-survive) are part of the <PerHeapHistory> data (MarkStack/FQ/Handles are marking stack variables, finalize queue and GC handles respectively). One of the perf improvements for [Server GC](#Server-GC) we made in .NET 5 is to balance work on the GC threads when we mark the OldGen roots since this usually causes the largest promotion amount. So if you see this number very unbalanced in your app, upgrading to .NET 5 would help.
1118+
How many bytes promoted by each GC thread due to various kinds of [roots](#What-makes-an-object-survive) are part of the PerHeapHistory data -MarkStack/FQ/Handles are marking stack variables, finalize queue and GC handles respectively and MarkOldGen indicates the number of bytes promoted due to references coming from older generations. So for example, if you are doing a gen1 GC, this is how much gen2 objects holding onto gen0/gen1 objects to make them survive. One of the perf improvements for [Server GC](#Server-GC) we made in .NET 5 is to balance work on the GC threads when we mark the OldGen roots since this usually causes the largest promotion amount. So if you see this number very unbalanced in your app, upgrading to .NET 5 would help.
11121119

11131120
#### **Figuring out if the long GCs are due to GC work or not**
11141121

0 commit comments

Comments
 (0)