private void PrintDiff(int topN, StringAnalysisResult res, StringAnalysisResult res2) { var top = res.StringCounts.OrderByDescending(kvp => kvp.Value.InstanceCount).Take(topN).ToDictionary(x => x.Key, x => x.Value); var top2 = res2.StringCounts.OrderByDescending(kvp => kvp.Value.InstanceCount).Take(topN).ToDictionary(x => x.Key, x => x.Value); HashSet <string> uniqueStringValues = new HashSet <string>(top.Select(x => x.Key).Concat(top2.Select(x => x.Key)).ToArray()); List <StringDiff> diffs = new List <StringDiff>(); foreach (var stringValue in uniqueStringValues) { ObjectStatistics stat = null; ObjectStatistics stat2 = null; top.TryGetValue(stringValue, out stat); top2.TryGetValue(stringValue, out stat2); diffs.Add(new StringDiff { DiffInBytes = (stat2 != null ? stat2.AllocatedInBytes : 0) - (stat != null ? stat.AllocatedInBytes : 0), InstanceDiffCount = (stat2 != null ? stat2.InstanceCount : 0) - (stat != null ? stat.InstanceCount : 0), Stat = stat, Stat2 = stat2, Value = stringValue }); } var sortedDiffs = diffs.OrderByDescending(x => Math.Abs(x.DiffInBytes)).ToArray(); Console.WriteLine("String Allocation Diff Statistics"); string fmtString = "{0,-12:N0}\t{1,17:N0}\t{2,-11:N0}\t{3,-11:N0}\t{4,-17:N0}\t{5,-18:N0}\t{6}"; OutputStringWriter.FormatAndWrite(fmtString, $"Delta({DisplayUnit})", "Delta(Instances)", "Instances", "Instances2", $"Allocated({DisplayUnit})", $"Allocated2({DisplayUnit})", "Value"); long displayUnitDiv = (long)DisplayUnit; foreach (var diff in sortedDiffs) { OutputStringWriter.FormatAndWrite(fmtString, diff.DiffInBytes / displayUnitDiv, diff.InstanceDiffCount, diff?.Stat?.InstanceCount, diff?.Stat2?.InstanceCount, diff?.Stat?.AllocatedInBytes / displayUnitDiv, diff?.Stat2?.AllocatedInBytes / displayUnitDiv, GetShortString(diff.Value)); } var deltaCount = res2.StringObjectCount - res.StringObjectCount; var deltaWaste = res2.StringWasteInBytes - res.StringWasteInBytes; var deltaBytes = res2.StringsAllocatedInBytes - res.StringsAllocatedInBytes; OutputStringWriter.FormatAndWrite(fmtString, deltaBytes / displayUnitDiv, deltaCount, res.StringObjectCount, res2.StringObjectCount, res.StringsAllocatedInBytes / displayUnitDiv, res2.StringsAllocatedInBytes / displayUnitDiv, "Strings(Total)"); }
static StringAnalysisResult Analyze(ClrHeap heap, bool bLiveOnly) { var stringUsages = new Dictionary <string, ObjectStatistics>(); foreach (var instance in GetObjectAddresses(heap, bLiveOnly)) { var type = heap.GetObjectType(instance); if (type != null && type.IsString) { var value = (string)type.GetValue(instance); ObjectStatistics countAndSize = null; if (stringUsages.TryGetValue(value, out countAndSize)) { countAndSize.InstanceCount++; } else { var size = type.GetSize(instance); stringUsages[value] = new ObjectStatistics { SizePerInstance = (long)size, InstanceCount = 1, SampleAddress = instance }; } } } var stringWasteInBytes = stringUsages.Values.Sum(o => (long)o.SizePerInstance * (long)(o.InstanceCount - 1)); var stringObjectCount = stringUsages.Values.Sum(o => (long)o.InstanceCount); var stringsAllocatedBytes = stringUsages.Values.Sum(o => (long)o.AllocatedInBytes); return(new StringAnalysisResult { StringCounts = stringUsages, StringWasteInBytes = stringWasteInBytes, StringObjectCount = stringObjectCount, StringsAllocatedInBytes = stringsAllocatedBytes }); }