在c#中查看垃圾收集历史(VS2015) [英] Viewing garbage collection history in c# (VS2015)

查看:201
本文介绍了在c#中查看垃圾收集历史(VS2015)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我运行我的应用程序时,无法预见和意料不到的垃圾收集活动量显示在进程内存图中,这让我想知道程序中的哪个地方是垃圾产生的,因为我觉得我没有内存泄漏在程序中。有人可以告诉我是否有办法查看我的代码中产生垃圾的部分(或行)吗?



预先感谢。

解决方案

你可以做的是使用微软的



在示例中,我选择了一个名为ConsoleApplication1的进程,您需要修改它。您可以增强它以定期拍摄快照,创建差异等。



以下是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;

//在条目的大小
上添加实时排序view.SortDescriptions.Add(new SortDescription(nameof(Entry.Size),ListSortDirection.Descending));
((ICollectionViewLiveShaping)视图).IsLiveSorting = true;

//每1000毫秒刷新
_timer.Interval = TimeSpan.FromMilliseconds(1000);
_timer.Tick + =(s,e)=>
{
// TODO:用进程名称替换ConsoleApplication1
RefreshHeap(ConsoleApplication1);
};
_timer.Start();
}

private void RefreshHeap(string processName)
{
var process = Process.GetProcessesByName(processName).FirstOrDefault();
if(process == null)
{
_entries.Clear();
return;
}

//需要使用Microsoft.Diagnostics.Runtime
(DataTarget target = DataTarget.AttachToProcess(process.Id,1000,AttachFlag.Passive))
{
//检查位元
if(Environment.Is64BitProcess!=(target.PointerSize == 8))
{
_entries.Clear();
return;
}

//读取一组新条目
var entries = ReadHeap(target.ClrVersions [0] .CreateRuntime());

//冻结旧的一组条目
var toBeRemoved = _entries.ToList();

//合并更新的条目并创建新条目
foreach(条目中的var条目值)
{
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);



//清除旧条目
toBeRemoved.ForEach(e => _entries.Remove(e));



//读取堆并为每个CLR类型构造一个条目列表
private static Dictionary< ClrType,Entry> ReadHeap(ClrRuntime运行时)
{
ClrHeap heap = runtime.GetHeap();
var entries = new Dictionary< ClrType,Entry>();
尝试
{
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;

输入条目;
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
{
//如果进程正在死亡,则会发生异常
}
返回条目;
}
}

公共类入口:INotifyPropertyChanged
{
private long _size;
private int _count;

公共事件PropertyChangedEventHandler PropertyChanged;
public ClrType Type {get;组; }

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




$ b这里是MainWindow.xaml: p>

 < Window x:Class =WpfApplication1.MainWindow
xmlns =http://schemas.microsoft.com / winfx / 2006 / xaml / presentation
xmlns:x =http://schemas.microsoft.com/winfx/2006/xaml
Title =MainWindowHeight =350Width = 525 >
<网格>
< DataGrid x:Name =_ gridAutoGenerateColumns =FalseIsReadOnly =True>
< DataGrid.Columns>
< DataGridTextColumn Binding ={Binding Size}Header =SizeWidth =2 */>
< DataGridTextColumn Binding ={Binding Count}Header =CountWidth =*/>
< DataGridTextColumn Binding ={Binding Type}Header =TypeWidth =10 */>
< /DataGrid.Columns>
< / DataGrid>
< / Grid>
< / Window>


A unforeseen and unanticipated amount of garbage collection activity is shown in the 'Process Memory' graph when I run my application which makes me want to know where in the program is the garbage generated as I don't feel that I have any memory leaks in the program. Can someone please tell me if there is a way to view the parts (or lines) of my code where garbage is generated?

Thanks in advance.

解决方案

What you could do is use Microsoft's CLR MD, a runtime process and crash dump introspection library. With this tool, you can program your own debugging tool precisely tailored to your needs, to determine what's in your app process memory.

You can install this library easily from Nuget, it's called Microsoft.Diagnostics.Runtime.Latest.

I have provided a small WPF sample that displays and refreshes every second all types used by a process, the number of instances of a type, and the size it uses in memory. This is what the tool looks like, it's live sorted on the Size column, so you can see what types eat up the most:

In the sample, I've chosen a process named "ConsoleApplication1", you'll need to adapt that. You could enhance it to take snapshot periodically, build diffs, etc.

Here is the 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; } }
    }
}

Here is the 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>

这篇关于在c#中查看垃圾收集历史(VS2015)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆