2017-05-08 22 views
5

Nieprzewidziane i nieprzewidziane ilości czynności związanych ze zbieraniem odpadów są wyświetlane na wykresie "Proces pamięci" po uruchomieniu aplikacji, co powoduje, że chcę wiedzieć, gdzie w programie są śmieci generowane jako Nie wydaje mi się, żebym miał jakieś wycieki pamięci w programie. Czy ktoś może mi powiedzieć, czy istnieje sposób wyświetlenia części (lub linii) mojego kodu, w którym generowane są śmieci?Przeglądanie historii pobierania pamięci w języku C# (VS2015)

Z góry dziękuję.

+0

Państwo określili go [vs2015]. Rozważ [używając go] (https://blogs.msdn.microsoft.com/visualstudioalm/2014/04/02/diagnosing-memory-issues-with-the-new-memory-usage-tool-in-visual-studio/), powinno to zostać szybko przeanalizowane poprzez porównanie migawek. –

Odpowiedz

7

Prawie każdy profiler pamięci pokaże te informacje. Po prostu znajdź listę "Martwych obiektów" pomiędzy dwiema migawkami i to jest lista "śmieci", która została wygenerowana i będzie musiała zostać zebrana przez GC.

Osobiście używam DotMemory przez JetBrains.

Na przykład z następującym programem

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(); 
     } 
    } 
} 

Dało mi wyjście jak enter image description here

Więc widać między dwoma migawek (czyli gdzie około 5 sekund od siebie) 218,242 ciągi, char [ ] s, a obiekty programu są gromadzone przez moduł do zbierania śmieci. i klikając łańcuchy, widzimy stosy wywołań, w których obiekty zostały utworzone. (zwróć uwagę, że musisz włączyć opcję "zbieraj dane przydziału", aby zobaczyć te stosy wywołań, bez uzyskiwania całkowitych liczb, ale nie w miejscu, z którego pochodzą)

7

Co można zrobić, to korzystać z Microsoft's CLR MD, środowiska wykonawczego biblioteka introspekcji procesu i awarii. Za pomocą tego narzędzia możesz zaprogramować własne narzędzie do debugowania dostosowane do Twoich potrzeb, aby określić, co znajduje się w pamięci procesu aplikacji.

Możesz łatwo zainstalować tę bibliotekę z Nuget, nazywa się Microsoft.Diagnostics.Runtime.Latest.

Podałem małą próbkę WPF, która wyświetla i odświeża co sekundę wszystkie typy używane przez proces, liczbę wystąpień typu i rozmiar, którego używa w pamięci. To właśnie narzędzie, jak wygląda to na żywo klasyfikowane na kolumnie rozmiarów, dzięki czemu można zobaczyć, jakie rodzaje jedzenia się najbardziej:

enter image description here

W próbce, wybrałem proces o nazwie " ConsoleApplication1 ", musisz to zaadaptować. Można zwiększyć go do podjęcia migawkę okresowo, budować dyferencjału, itp

Oto MainWindow.xaml.cs:

public partial class MainWindow : Window 
{ 
    private DispatcherTimer _timer = new DispatcherTimer(); 
    private ObservableCollection<Entry> _entries = new ObservableCollection<Entry>(); 

    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<ClrType, Entry> ReadHeap(ClrRuntime runtime) 
    { 
     ClrHeap heap = runtime.GetHeap(); 
     var entries = new Dictionary<ClrType, Entry>(); 
     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; } } 
    } 
} 

Oto MainWindow.xaml:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <DataGrid x:Name="_grid" AutoGenerateColumns="False" IsReadOnly="True" > 
      <DataGrid.Columns> 
       <DataGridTextColumn Binding="{Binding Size}" Header="Size" Width="2*" /> 
       <DataGridTextColumn Binding="{Binding Count}" Header="Count" Width="*" /> 
       <DataGridTextColumn Binding="{Binding Type}" Header="Type" Width="10*" /> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 
</Window> 
+0

Nigdy wcześniej nie oglądałem tej biblioteki, wygląda całkiem zgrabnie! Czy może ci również powiedzieć, gdzie alokacje gdzie lub czy tylko zapewnia całkowitą liczbę? –

+0

@ScottChamberlain - dostępnych jest wiele informacji, ale nie sądzę, abyśmy mogli określić, jaki kod wygenerował instancję określonego typu, jeśli o to ci chodzi. W rzeczywistości jest bardzo podobny do tego, co można zrobić za pomocą konsoli debuggera (np. Windbg), ale programowo iz .NET. –

0

systemu .GC zawiera obiekt do zbierania śmieci, a istnieje wiele metod statycznych, które można wykorzystać do bezpośredniej kontroli nad procesem.

void GC :: Collect() wywołuje GC dla wszystkich pokoleń, a void GC :: honor (int generacji) powołuje go tylko do i włącznie generowanie okreslic.

inny

użyć tego polecenia ! Eeheap -GC w terminalu