在c#中查看垃圾收集历史记录(VS2015)

当我运行我的应用程序时,“进程内存”图中显示了无法预料和意外数量的垃圾收集活动,这让我想知道程序中生成的垃圾在哪里,因为我觉得我没有任何内存泄漏该程序。 有人可以告诉我是否有办法查看我的代码中生成垃圾的部分(或行)?

提前致谢。

几乎任何内存分析器都会显示此信息。 只需在两个快照之间查找“死对象”列表,这是生成的“垃圾”列表,需要由GC收集。

我个人使用JetBrains的DotMemory 。

例如,使用以下程序

using System; namespace SandboxConsole { class Program { private int _test; static void Main(string[] args) { var rnd = new Random(); while (true) { var obj = new Program(); obj._test = rnd.Next(); Console.WriteLine(obj); } } public override string ToString() { return _test.ToString(); } } } 

它给了我一个输出 在此处输入图像描述

因此,您可以在两个快照(相隔约5秒)之间看到218,242个字符串,char []和由垃圾收集器收集的Program对象。 通过单击字符串,我们可以看到创建对象的调用堆栈。 (注意,您需要启用“收集分配数据”选项以查看这些调用堆栈,如果没有它,您将获得总数而不是对象来自的位置)

你可以做的是使用Microsoft的CLR MD ,一个运行时进程和崩溃转储内省库。 使用此工具,您可以根据自己的需要编写自己的调试工具,以确定应用程序进程内存中的内容。

您可以从Nuget轻松安装此库,它名为Microsoft.Diagnostics.Runtime.Latest 。

我提供了一个小的WPF示例,它显示和刷新进程使用的所有类型,类型的实例数以及它在内存中使用的大小。 这就是该工具的样子,它在“ 大小”列上实时排序,因此您可以看到最常吃的类型:

在此处输入图像描述

在示例中,我选择了一个名为“ConsoleApplication1”的进程,您需要对其进行调整。 您可以增强它以定期拍摄快照,构建差异等。

这是MainWindow.xaml.cs:

 public partial class MainWindow : Window { private DispatcherTimer _timer = new DispatcherTimer(); private ObservableCollection _entries = new ObservableCollection(); public MainWindow() { InitializeComponent(); var view = CollectionViewSource.GetDefaultView(_entries); _grid.ItemsSource = view; // add live sorting on entry's Size view.SortDescriptions.Add(new SortDescription(nameof(Entry.Size), ListSortDirection.Descending)); ((ICollectionViewLiveShaping)view).IsLiveSorting = true; // refresh every 1000 ms _timer.Interval = TimeSpan.FromMilliseconds(1000); _timer.Tick += (s, e) => { // TODO: replace "ConsoleApplication1" by your process name RefreshHeap("ConsoleApplication1"); }; _timer.Start(); } private void RefreshHeap(string processName) { var process = Process.GetProcessesByName(processName).FirstOrDefault(); if (process == null) { _entries.Clear(); return; } // needs Microsoft.Diagnostics.Runtime using (DataTarget target = DataTarget.AttachToProcess(process.Id, 1000, AttachFlag.Passive)) { // check bitness if (Environment.Is64BitProcess != (target.PointerSize == 8)) { _entries.Clear(); return; } // read new set of entries var entries = ReadHeap(target.ClrVersions[0].CreateRuntime()); // freeze old set of entries var toBeRemoved = _entries.ToList(); // merge updated entries and create new entries foreach (var entry in entries.Values) { var existing = _entries.FirstOrDefault(e => e.Type == entry.Type); if (existing != null) { existing.Count = entry.Count; existing.Size = entry.Size; toBeRemoved.Remove(entry); } else { _entries.Add(entry); } } // purge old entries toBeRemoved.ForEach(e => _entries.Remove(e)); } } // read the heap and construct a list of entries per CLR type private static Dictionary ReadHeap(ClrRuntime runtime) { ClrHeap heap = runtime.GetHeap(); var entries = new Dictionary(); try { foreach (var seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { ClrType type = heap.GetObjectType(obj); if (type == null) continue; Entry entry; if (!entries.TryGetValue(type, out entry)) { entry = new Entry(); entry.Type = type; entries.Add(type, entry); } entry.Count++; entry.Size += (long)type.GetSize(obj); } } } catch { // exceptions can happen if the process is dying } return entries; } } public class Entry : INotifyPropertyChanged { private long _size; private int _count; public event PropertyChangedEventHandler PropertyChanged; public ClrType Type { get; set; } public int Count { get { return _count; } set { if (_count != value) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); _count = value; } } } public long Size { get { return _size; } set { if (_size != value) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Size))); _size = value; } } } } 

这是MainWindow.xaml:

            

System.GC包含垃圾收集对象,您可以使用许多静态方法直接控制该进程。

void GC :: Collect()为所有代调用GC,而void GC :: Collect(int Generation)仅调用它,包括您指定的代。

其他

使用此命令终端中的eeheap -gc