什么是ClrMD相当于!DumpHeap -live?

时间:2016-02-08 11:35:46

标签: .net clr clrmd

ClrMD 是检查实时可执行文件或内存转储的绝佳工具。要将所有托管堆对象转储为摘要,可以使用

https://blogs.msdn.microsoft.com/dotnet/2013/05/01/net-crash-dump-and-live-process-inspection/

var stats = from o in heap.EnumerateObjects()
            let t = heap.GetObjectType(o)
            group o by t into g
            let size = g.Sum(o => (uint)g.Key.GetSize(o))
            orderby size
            select new
            {
                Name = g.Key.Name,
                Size = size,
                Count = g.Count()
            };

foreach (var item in stats)
    Console.WriteLine("{0,12:n0} {1,12:n0} {2}", item.Size, item.Count, item.Name);

效果很好,相当于带有 SOS 扩展的 Windbg 中的!DumpHeap -stat 。对于回归测试,通常需要在测试后检查物体是否泄漏。这看起来非常适合上面的代码,但遗憾的是它会产生误报,因为可能存在不再有根的对象,但它们仍然在摘要中报告。这可能导致报告泄漏,尽管没有。

SOS 通过添加到!DumpHeap -live 开关来缓解此问题。什么是等效的 ClrMD 代码才能获得实时对象,因此回归测试只会因真正的原因而失败?

我想我需要使用 ClrType.EnumerateRefsOfObjectCarefully 以递归方式遍历堆栈,直到找到根对象( ClrHeap.EnumerateRoots ),但该方法需要许多临时Hashsets跟踪递归对象图。这是唯一的方法吗?或者MS内部某处已经有一个正确且性能良好的样本( PerfView 源?)。

1 个答案:

答案 0 :(得分:1)

我在https://harshaprojects.wordpress.com/2015/12/29/clr-md-analyzing-live-process/#comment-32找到了一些不错的博客,其中包含了我所追求的内容。为了完整起见,我在这里发布代码。

ObjectSet类是一个内存效率更高的HashSet,因为HashSet在作者机器上引起了OOM。我使用EnumerateRefsOfObjectCarefully,因为这与PerfView使用的方法相同(我猜的原因)。

现在这也是我的开源WMemoryProfiler v.2.2

的一部分

https://wmemoryprofiler.codeplex.com/releases/view/619764

允许您选择带有SOS或ClrMD的Windbg来自动分析流程。

    private static ObjectSet GetLiveObjects(ClrHeap heap)
        {
            ObjectSet considered = new ObjectSet(heap);
            Stack<ulong> eval = new Stack<ulong>();

            foreach (var root in heap.EnumerateRoots())
                eval.Push(root.Object);

            while (eval.Count > 0)
            {
                ulong obj = eval.Pop();
                if (considered.Contains(obj))
                    continue;

                considered.Add(obj);

                var type = heap.GetObjectType(obj);
                if (type == null)  // Only if heap corruption
                    continue;

                type.EnumerateRefsOfObjectCarefully(obj, delegate(ulong child, int offset)
                {
                    if (child != 0 && !considered.Contains(child))
                        eval.Push(child);
                });
            }

            return considered;
        }

    class DumpDiffEntry
    {
        public ClrType Type;
        public int Count;
        public long Size;
    }

    class Entry
    {
        public string Name;
        public int Count;
        public ulong Size;
    }
    class ObjectSet
    {
        struct Entry
        {
            public ulong High;
            public ulong Low;
            public int Index;
        }

        public ObjectSet(ClrHeap heap)
        {
            m_shift = IntPtr.Size == 4 ? 3 : 4;
            int count = heap.Segments.Count;

            m_data = new BitArray[count];
            m_entries = new Entry[count];
#if DEBUG
            ulong last = 0;
#endif

            for (int i = 0; i < count; ++i)
            {
                var seg = heap.Segments[i];
#if DEBUG
                Debug.Assert(last < seg.Start);
                last = seg.Start;
#endif

                m_data[i] = new BitArray(GetBitOffset(seg.Length));
                m_entries[i].Low = seg.Start;
                m_entries[i].High = seg.End;
                m_entries[i].Index = i;
            }
        }

        public void Add(ulong value)
        {
            if (value == 0)
            {
                m_zero = true;
                return;
            }

            int index = GetIndex(value);
            if (index == -1)
                return;

            int offset = GetBitOffset(value - m_entries[index].Low);

            m_data[index].Set(offset, true);
        }

        public bool Contains(ulong value)
        {
            if (value == 0)
                return m_zero;


            int index = GetIndex(value);
            if (index == -1)
                return false;

            int offset = GetBitOffset(value - m_entries[index].Low);

            return m_data[index][offset];
        }

        public int Count
        {
            get
            {
                // todo, this is nasty.
                int count = 0;
                foreach (var set in m_data)
                    foreach (bool bit in set)
                        if (bit) count++;

                return count;
            }
        }

        private int GetBitOffset(ulong offset)
        {
            Debug.Assert(offset < int.MaxValue);
            return GetBitOffset((int)offset);
        }

        private int GetBitOffset(int offset)
        {
            return offset >> m_shift;
        }

        private int GetIndex(ulong value)
        {
            int low = 0;
            int high = m_entries.Length - 1;

            while (low <= high)
            {
                int mid = (low + high) >> 1;
                if (value < m_entries[mid].Low)
                    high = mid - 1;
                else if (value > m_entries[mid].High)
                    low = mid + 1;
                else
                    return mid;
            }

            // Outside of the heap.
            return -1;
        }

        BitArray[] m_data;
        Entry[] m_entries;
        int m_shift;
        bool m_zero;
    }