@@ -15,15 +15,16 @@ public struct GcStats : IEquatable<GcStats>
15
15
private static readonly Func < long > GetAllocatedBytesForCurrentThreadDelegate = CreateGetAllocatedBytesForCurrentThreadDelegate ( ) ;
16
16
private static readonly Func < bool , long > GetTotalAllocatedBytesDelegate = CreateGetTotalAllocatedBytesDelegate ( ) ;
17
17
18
- public static readonly GcStats Empty = new GcStats ( 0 , 0 , 0 , 0 , 0 ) ;
18
+ public static readonly GcStats Empty = new GcStats ( 0 , 0 , 0 , 0 , 0 , 0 ) ;
19
19
20
- private GcStats ( int gen0Collections , int gen1Collections , int gen2Collections , long allocatedBytes , long totalOperations )
20
+ private GcStats ( int gen0Collections , int gen1Collections , int gen2Collections , long allocatedBytes , long totalOperations , long survivedBytes )
21
21
{
22
22
Gen0Collections = gen0Collections ;
23
23
Gen1Collections = gen1Collections ;
24
24
Gen2Collections = gen2Collections ;
25
25
AllocatedBytes = allocatedBytes ;
26
26
TotalOperations = totalOperations ;
27
+ SurvivedBytes = survivedBytes ;
27
28
}
28
29
29
30
// did not use array here just to avoid heap allocation
@@ -37,6 +38,7 @@ private GcStats(int gen0Collections, int gen1Collections, int gen2Collections, l
37
38
private long AllocatedBytes { get ; }
38
39
39
40
public long TotalOperations { get ; }
41
+ public long SurvivedBytes { get ; }
40
42
41
43
public long BytesAllocatedPerOperation
42
44
{
@@ -60,7 +62,8 @@ public long BytesAllocatedPerOperation
60
62
left . Gen1Collections + right . Gen1Collections ,
61
63
left . Gen2Collections + right . Gen2Collections ,
62
64
left . AllocatedBytes + right . AllocatedBytes ,
63
- left . TotalOperations + right . TotalOperations ) ;
65
+ left . TotalOperations + right . TotalOperations ,
66
+ left . SurvivedBytes + right . SurvivedBytes ) ;
64
67
}
65
68
66
69
public static GcStats operator - ( GcStats left , GcStats right )
@@ -70,11 +73,15 @@ public long BytesAllocatedPerOperation
70
73
Math . Max ( 0 , left . Gen1Collections - right . Gen1Collections ) ,
71
74
Math . Max ( 0 , left . Gen2Collections - right . Gen2Collections ) ,
72
75
Math . Max ( 0 , left . AllocatedBytes - right . AllocatedBytes ) ,
73
- Math . Max ( 0 , left . TotalOperations - right . TotalOperations ) ) ;
76
+ Math . Max ( 0 , left . TotalOperations - right . TotalOperations ) ,
77
+ Math . Max ( 0 , left . SurvivedBytes - right . SurvivedBytes ) ) ;
74
78
}
75
79
76
80
public GcStats WithTotalOperations ( long totalOperationsCount )
77
- => this + new GcStats ( 0 , 0 , 0 , 0 , totalOperationsCount ) ;
81
+ => this + new GcStats ( 0 , 0 , 0 , 0 , totalOperationsCount , 0 ) ;
82
+
83
+ public GcStats WithSurvivedBytes ( bool getBytes )
84
+ => this + new GcStats ( 0 , 0 , 0 , 0 , 0 , GetTotalBytes ( getBytes ) ) ;
78
85
79
86
public int GetCollectionsCount ( int generation )
80
87
{
@@ -112,6 +119,7 @@ public static GcStats ReadInitial()
112
119
GC . CollectionCount ( 1 ) ,
113
120
GC . CollectionCount ( 2 ) ,
114
121
allocatedBytes ,
122
+ 0 ,
115
123
0 ) ;
116
124
}
117
125
@@ -125,12 +133,31 @@ public static GcStats ReadFinal()
125
133
// this will force GC.Collect, so we want to do this after collecting collections counts
126
134
// to exclude this single full forced collection from results
127
135
GetAllocatedBytes ( ) ,
136
+ 0 ,
128
137
0 ) ;
129
138
}
130
139
131
140
[ PublicAPI ]
132
141
public static GcStats FromForced ( int forcedFullGarbageCollections )
133
- => new GcStats ( forcedFullGarbageCollections , forcedFullGarbageCollections , forcedFullGarbageCollections , 0 , 0 ) ;
142
+ => new GcStats ( forcedFullGarbageCollections , forcedFullGarbageCollections , forcedFullGarbageCollections , 0 , 0 , 0 ) ;
143
+
144
+ private static long GetTotalBytes ( bool actual )
145
+ {
146
+ if ( ! actual )
147
+ return 0 ;
148
+
149
+ if ( RuntimeInformation . IsFullFramework ) // it can be a .NET app consuming our .NET Standard package
150
+ {
151
+ AppDomain . MonitoringIsEnabled = true ;
152
+
153
+ // Enforce GC.Collect here just to make sure we get accurate results
154
+ GC . Collect ( ) ;
155
+ return AppDomain . CurrentDomain . MonitoringSurvivedMemorySize ;
156
+ }
157
+
158
+ GC . Collect ( ) ;
159
+ return GC . GetTotalMemory ( true ) ;
160
+ }
134
161
135
162
private static long GetAllocatedBytes ( )
136
163
{
@@ -171,7 +198,7 @@ private static Func<bool, long> CreateGetTotalAllocatedBytesDelegate()
171
198
}
172
199
173
200
public string ToOutputLine ( )
174
- => $ "{ ResultsLinePrefix } { Gen0Collections } { Gen1Collections } { Gen2Collections } { AllocatedBytes } { TotalOperations } ";
201
+ => $ "{ ResultsLinePrefix } { Gen0Collections } { Gen1Collections } { Gen2Collections } { AllocatedBytes } { TotalOperations } { SurvivedBytes } ";
175
202
176
203
public static GcStats Parse ( string line )
177
204
{
@@ -183,12 +210,13 @@ public static GcStats Parse(string line)
183
210
|| ! int . TryParse ( measurementSplit [ 1 ] , out int gen1 )
184
211
|| ! int . TryParse ( measurementSplit [ 2 ] , out int gen2 )
185
212
|| ! long . TryParse ( measurementSplit [ 3 ] , out long allocatedBytes )
186
- || ! long . TryParse ( measurementSplit [ 4 ] , out long totalOperationsCount ) )
213
+ || ! long . TryParse ( measurementSplit [ 4 ] , out long totalOperationsCount )
214
+ || ! long . TryParse ( measurementSplit [ 5 ] , out long survivedBytes ) )
187
215
{
188
216
throw new NotSupportedException ( "Invalid string" ) ;
189
217
}
190
218
191
- return new GcStats ( gen0 , gen1 , gen2 , allocatedBytes , totalOperationsCount ) ;
219
+ return new GcStats ( gen0 , gen1 , gen2 , allocatedBytes , totalOperationsCount , survivedBytes ) ;
192
220
}
193
221
194
222
public override string ToString ( ) => ToOutputLine ( ) ;
@@ -222,7 +250,13 @@ private static long CalculateAllocationQuantumSize()
222
250
return result ;
223
251
}
224
252
225
- public bool Equals ( GcStats other ) => Gen0Collections == other . Gen0Collections && Gen1Collections == other . Gen1Collections && Gen2Collections == other . Gen2Collections && AllocatedBytes == other . AllocatedBytes && TotalOperations == other . TotalOperations ;
253
+ public bool Equals ( GcStats other ) =>
254
+ Gen0Collections == other . Gen0Collections
255
+ && Gen1Collections == other . Gen1Collections
256
+ && Gen2Collections == other . Gen2Collections
257
+ && AllocatedBytes == other . AllocatedBytes
258
+ && TotalOperations == other . TotalOperations
259
+ && SurvivedBytes == other . SurvivedBytes ;
226
260
227
261
public override bool Equals ( object obj ) => obj is GcStats other && Equals ( other ) ;
228
262
0 commit comments