@@ -15,15 +15,16 @@ public struct GcStats : IEquatable<GcStats>
1515 private static readonly Func < long > GetAllocatedBytesForCurrentThreadDelegate = CreateGetAllocatedBytesForCurrentThreadDelegate ( ) ;
1616 private static readonly Func < bool , long > GetTotalAllocatedBytesDelegate = CreateGetTotalAllocatedBytesDelegate ( ) ;
1717
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 ) ;
1919
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 )
2121 {
2222 Gen0Collections = gen0Collections ;
2323 Gen1Collections = gen1Collections ;
2424 Gen2Collections = gen2Collections ;
2525 AllocatedBytes = allocatedBytes ;
2626 TotalOperations = totalOperations ;
27+ SurvivedBytes = survivedBytes ;
2728 }
2829
2930 // did not use array here just to avoid heap allocation
@@ -37,6 +38,7 @@ private GcStats(int gen0Collections, int gen1Collections, int gen2Collections, l
3738 private long AllocatedBytes { get ; }
3839
3940 public long TotalOperations { get ; }
41+ public long SurvivedBytes { get ; }
4042
4143 public long BytesAllocatedPerOperation
4244 {
@@ -60,7 +62,8 @@ public long BytesAllocatedPerOperation
6062 left . Gen1Collections + right . Gen1Collections ,
6163 left . Gen2Collections + right . Gen2Collections ,
6264 left . AllocatedBytes + right . AllocatedBytes ,
63- left . TotalOperations + right . TotalOperations ) ;
65+ left . TotalOperations + right . TotalOperations ,
66+ left . SurvivedBytes + right . SurvivedBytes ) ;
6467 }
6568
6669 public static GcStats operator - ( GcStats left , GcStats right )
@@ -70,11 +73,15 @@ public long BytesAllocatedPerOperation
7073 Math . Max ( 0 , left . Gen1Collections - right . Gen1Collections ) ,
7174 Math . Max ( 0 , left . Gen2Collections - right . Gen2Collections ) ,
7275 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 ) ) ;
7478 }
7579
7680 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 ) ) ;
7885
7986 public int GetCollectionsCount ( int generation )
8087 {
@@ -112,6 +119,7 @@ public static GcStats ReadInitial()
112119 GC . CollectionCount ( 1 ) ,
113120 GC . CollectionCount ( 2 ) ,
114121 allocatedBytes ,
122+ 0 ,
115123 0 ) ;
116124 }
117125
@@ -125,12 +133,31 @@ public static GcStats ReadFinal()
125133 // this will force GC.Collect, so we want to do this after collecting collections counts
126134 // to exclude this single full forced collection from results
127135 GetAllocatedBytes ( ) ,
136+ 0 ,
128137 0 ) ;
129138 }
130139
131140 [ PublicAPI ]
132141 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+ }
134161
135162 private static long GetAllocatedBytes ( )
136163 {
@@ -171,7 +198,7 @@ private static Func<bool, long> CreateGetTotalAllocatedBytesDelegate()
171198 }
172199
173200 public string ToOutputLine ( )
174- => $ "{ ResultsLinePrefix } { Gen0Collections } { Gen1Collections } { Gen2Collections } { AllocatedBytes } { TotalOperations } ";
201+ => $ "{ ResultsLinePrefix } { Gen0Collections } { Gen1Collections } { Gen2Collections } { AllocatedBytes } { TotalOperations } { SurvivedBytes } ";
175202
176203 public static GcStats Parse ( string line )
177204 {
@@ -183,12 +210,13 @@ public static GcStats Parse(string line)
183210 || ! int . TryParse ( measurementSplit [ 1 ] , out int gen1 )
184211 || ! int . TryParse ( measurementSplit [ 2 ] , out int gen2 )
185212 || ! 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 ) )
187215 {
188216 throw new NotSupportedException ( "Invalid string" ) ;
189217 }
190218
191- return new GcStats ( gen0 , gen1 , gen2 , allocatedBytes , totalOperationsCount ) ;
219+ return new GcStats ( gen0 , gen1 , gen2 , allocatedBytes , totalOperationsCount , survivedBytes ) ;
192220 }
193221
194222 public override string ToString ( ) => ToOutputLine ( ) ;
@@ -222,7 +250,13 @@ private static long CalculateAllocationQuantumSize()
222250 return result ;
223251 }
224252
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 ;
226260
227261 public override bool Equals ( object obj ) => obj is GcStats other && Equals ( other ) ;
228262
0 commit comments