private void AssertPathIsCorrect(ClrHeap heap, ClrObject[] path, ulong source, ulong target) { Assert.NotNull(path); Assert.True(path.Length > 0); ClrObject first = path.First(); Assert.Equal(source, first.Address); for (int i = 0; i < path.Length - 1; i++) { ClrObject curr = path[i]; Assert.Equal(curr.Type, heap.GetObjectType(curr.Address)); IEnumerable <ClrObject> refs = curr.EnumerateObjectReferences(); ClrObject next = path[i + 1]; Assert.Contains(next, refs); } ClrObject last = path.Last(); Assert.Equal(last.Type, heap.GetObjectType(last.Address)); Assert.Equal(target, last.Address); }
private static void ShowObject(ClrHeap heap, ClrObject clrObject, string indent) { Console.WriteLine($"{indent} {clrObject.Type.Name} ({clrObject.HexAddress}) - gen{heap.GetGeneration(clrObject.Address)}"); foreach (var reference in clrObject.EnumerateObjectReferences()) { ShowObject(heap, reference, " "); } }
private void CalculateTypeStatisticsPhase2(ClrHeap heap, Regex typesToReportStatisticsOnRegex, HashSet <ulong> markedObjects, TypeStatistics typeStatistics) { typeStatistics.MemoryProfilingStopwatch.Start(); HashSet <ulong> visitedObjects = new HashSet <ulong>(); Queue <ClrObject> objectQueue = new Queue <ClrObject>(); // Start with exclusive = inclusive and walk the heap from roots looking for objects to subtract from this number. typeStatistics.ExclusiveRetainedBytes = typeStatistics.InclusiveRetainedBytes; foreach (ClrRoot root in heap.EnumerateRoots()) { // Interested only in roots outside of our marked inclusive graph. if (!markedObjects.Contains(root.Object)) { ClrObject rootObj = heap.GetObject(root.Object); objectQueue.Enqueue(rootObj); visitedObjects.Add(root.Object); while (objectQueue.Count > 0) { ClrObject obj = objectQueue.Dequeue(); if (obj.IsNull || obj.Type == null) { continue; } // We stop the walk when we see an object of a type we are reporting statistics on. if (!typesToReportStatisticsOnRegex.IsMatch(obj.Type.Name)) { if (markedObjects.Contains(obj.Address)) { // Not an object of a type we are reporting statistics on but it is part of the inclusive object graph. // This means that it must not be reported in the exclusive bytes. typeStatistics.ExclusiveRetainedBytes -= obj.Size; markedObjects.Remove(obj.Address); } // Follow all references. foreach (var reference in obj.EnumerateObjectReferences()) { if (!reference.IsNull && !visitedObjects.Contains(reference.Address)) { visitedObjects.Add(reference.Address); objectQueue.Enqueue(reference); } } } } } } typeStatistics.MemoryProfilingStopwatch.Stop(); }
public void EnumerateGCRefsArray() { using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump(); ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrModule module = heap.Runtime.GetMainModule(); ClrType mainType = module.GetTypeByName("GCRootTarget"); ClrObject obj = mainType.GetStaticObjectValue("TheRoot"); obj = obj.GetObjectField("Item1"); Assert.Equal("System.Object[]", obj.Type.Name); ClrObject[] refs = obj.EnumerateObjectReferences(false).ToArray(); Assert.Single(refs); Assert.Equal("DoubleRef", refs[0].Type.Name); }
/// <summary> /// Enumerates all objects reachable from GC roots in the given heap. Each object is returned exactly once. /// </summary> private IEnumerable <ClrObject> EnumerateRootedObjects(ClrHeap heap) { HashSet <ulong> visitedObjects = new HashSet <ulong>(); Queue <ClrObject> objectQueue = new Queue <ClrObject>(); foreach (ClrRoot root in heap.EnumerateRoots()) { if (!visitedObjects.Contains(root.Object)) { ClrObject rootObj = heap.GetObject(root.Object); objectQueue.Enqueue(rootObj); visitedObjects.Add(root.Object); if (rootObj.Type != null) { yield return(rootObj); } while (objectQueue.Count > 0) { ClrObject obj = objectQueue.Dequeue(); if (obj.IsNull || obj.Type == null) { continue; } // Follow all references. foreach (var reference in obj.EnumerateObjectReferences()) { if (!reference.IsNull && !visitedObjects.Contains(reference.Address)) { objectQueue.Enqueue(reference); visitedObjects.Add(reference.Address); yield return(reference); } } } } } }
private void CalculateTypeStatisticsPhase1(ClrObject rootObj, Regex typesToReportStatisticsOnRegex, HashSet <ulong> markedObjects, TypeStatistics typeStatistics) { if (!markedObjects.Contains(rootObj.Address)) { // Start the stopwatch only if we're looking at an unmarked object. Measuring the single HashSet.Contains() would have more // overhead than the actual duration of the operation. typeStatistics.MemoryProfilingStopwatch.Start(); Queue <ClrObject> objectQueue = new Queue <ClrObject>(); objectQueue.Enqueue(rootObj); markedObjects.Add(rootObj.Address); while (objectQueue.Count > 0) { ClrObject obj = objectQueue.Dequeue(); typeStatistics.InclusiveRetainedBytes += obj.Size; foreach (var reference in obj.EnumerateObjectReferences()) { if (!reference.IsNull && !markedObjects.Contains(reference.Address)) { // We follow references only as long as they point to BCL types or to types to report statistics on. // Otherwise we would transitively walk pretty much the entire managed heap and the number we report // would be meaningless. string referenceType = reference.Type.Name; if (referenceType.StartsWith("System.") || typesToReportStatisticsOnRegex.IsMatch(referenceType)) { markedObjects.Add(reference.Address); objectQueue.Enqueue(reference); } } } } typeStatistics.MemoryProfilingStopwatch.Stop(); } }
/// <summary> /// Enumerates all objects that this object references. /// </summary> /// <param name="carefully">if set to <c>true</c> [carefully].</param> /// <returns>An enumeration of object references.</returns> /// <inheritdoc /> public IEnumerable <IClrObject> EnumerateObjectReferences(bool carefully = false) => Object.EnumerateObjectReferences(carefully).Select(Converter.Convert);