Add column `persisted` and saving item to console from diff. (#5290)

diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/heap_diff.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/heap_diff.dart
index 0df4bee..871d5f4 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/heap_diff.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/heap_diff.dart
@@ -7,6 +7,7 @@
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
 import '../../../../../shared/analytics/metrics.dart';
+import '../../../../../shared/memory/adapted_heap_data.dart';
 import '../../../../../shared/memory/class_name.dart';
 import '../../../../../shared/primitives/utils.dart';
 import '../../../shared/heap/heap.dart';
@@ -81,7 +82,9 @@
 /// List of classes with per-class comparison between two heaps.
 class DiffHeapClasses extends HeapClasses<DiffClassStats>
     with FilterableHeapClasses<DiffClassStats> {
-  DiffHeapClasses(_HeapCouple couple) {
+  DiffHeapClasses(_HeapCouple couple)
+      : before = couple.younger.data,
+        after = couple.older.data {
     classesByName = subtractMaps<HeapClassName, SingleClassStats,
         SingleClassStats, DiffClassStats>(
       from: couple.younger.classes.classesByName,
@@ -95,6 +98,8 @@
   late final Map<HeapClassName, DiffClassStats> classesByName;
   late final List<DiffClassStats> classes =
       classesByName.values.toList(growable: false);
+  final AdaptedHeapData before;
+  final AdaptedHeapData after;
 
   @override
   void seal() {
@@ -158,28 +163,41 @@
     for (var code in allCodes) {
       final inBefore = codesBefore.contains(code);
       final inAfter = codesAfter.contains(code);
-      if (inBefore && inAfter) continue;
 
       final object = before.objectsByCodes[code] ?? after.objectsByCodes[code]!;
 
+      if (inAfter && inBefore) {
+        // We assume that state 'after' is what is most interesting for user
+        // about the retained size.
+        final excludeFromRetained =
+            after.objectsExcludedFromRetainedSize.contains(object.code);
+        persisted.countInstance(
+          object,
+          excludeFromRetained: excludeFromRetained,
+        );
+        continue;
+      }
+
       if (inBefore) {
         final excludeFromRetained =
-            before.notCountedInRetained.contains(object.code);
+            before.objectsExcludedFromRetainedSize.contains(object.code);
         deleted.countInstance(object, excludeFromRetained: excludeFromRetained);
         delta.uncountInstance(object, excludeFromRetained: excludeFromRetained);
         continue;
       }
       if (inAfter) {
         final excludeFromRetained =
-            after.notCountedInRetained.contains(object.code);
+            after.objectsExcludedFromRetainedSize.contains(object.code);
         created.countInstance(object, excludeFromRetained: excludeFromRetained);
         delta.countInstance(object, excludeFromRetained: excludeFromRetained);
         continue;
       }
+
       assert(false);
     }
     created.seal();
     deleted.seal();
+    persisted.seal();
     delta.seal();
     assert(
       delta.instanceCount == created.instanceCount - deleted.instanceCount,
@@ -188,6 +206,7 @@
 
   final created = ObjectSet();
   final deleted = ObjectSet();
+  final persisted = ObjectSet();
   final delta = ObjectSetStats();
 
   bool get isZero => delta.isZero;
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/sampler.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/sampler.dart
index d0f35c2..b26905a 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/sampler.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/sampler.dart
@@ -10,12 +10,13 @@
 import '../../../../../shared/memory/class_name.dart';
 import '../../../../../shared/vm_utils.dart';
 import '../../../shared/heap/heap.dart';
-import '../../../shared/primitives/instance_set_view.dart';
+import '../../../shared/primitives/instance_set_button.dart';
 
 class HeapClassSampler extends ClassSampler {
-  HeapClassSampler(this.objects, this.heap);
+  HeapClassSampler(this.objects, this.heap, this.heapClass);
 
-  final SingleClassStats objects;
+  final HeapClassName heapClass;
+  final ObjectSet objects;
   final AdaptedHeapData heap;
 
   IsolateRef get _mainIsolateRef =>
@@ -24,7 +25,7 @@
   Future<InstanceSet?> _liveInstances() async {
     final isolateId = _mainIsolateRef.id!;
 
-    final theClass = await findClass(isolateId, objects.heapClass);
+    final theClass = await findClass(isolateId, heapClass);
     if (theClass == null) return null;
 
     return await serviceManager.service!.getInstances(
@@ -41,7 +42,7 @@
     final instanceRef = instances?.firstWhereOrNull(
       (objRef) =>
           objRef is InstanceRef &&
-          objects.objects.objectsByCodes.containsKey(objRef.identityHashCode),
+          objects.objectsByCodes.containsKey(objRef.identityHashCode),
     ) as InstanceRef?;
 
     if (instanceRef == null) {
@@ -51,8 +52,7 @@
       return;
     }
 
-    final heapObject =
-        objects.objects.objectsByCodes[instanceRef.identityHashCode!]!;
+    final heapObject = objects.objectsByCodes[instanceRef.identityHashCode!]!;
 
     final heapSelection = HeapObjectSelection(heap, object: heapObject);
 
@@ -66,13 +66,13 @@
 
   @override
   bool get isEvalEnabled =>
-      objects.heapClass.classType(serviceManager.rootInfoNow().package) !=
+      heapClass.classType(serviceManager.rootInfoNow().package) !=
       ClassType.runtime;
 
   @override
   Future<void> manyLiveToConsole() async {
     serviceManager.consoleService.appendInstanceSet(
-      type: objects.heapClass.shortName,
+      type: heapClass.shortName,
       instanceSet: (await _liveInstances())!,
       isolateRef: _mainIsolateRef,
     );
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
index a5d22e6..a4e67db 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
@@ -6,20 +6,25 @@
 
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
+import '../../../../../shared/feature_flags.dart';
 import '../../../../../shared/globals.dart';
+import '../../../../../shared/memory/adapted_heap_data.dart';
 import '../../../../../shared/primitives/utils.dart';
 import '../../../../../shared/table/table.dart';
 import '../../../../../shared/table/table_data.dart';
 import '../../../../../shared/theme.dart';
 import '../../../../../shared/utils.dart';
+import '../../../shared/heap/heap.dart';
 import '../../../shared/primitives/simple_elements.dart';
 import '../../../shared/shared_memory_widgets.dart';
 import '../controller/heap_diff.dart';
+import 'instances.dart';
 
 enum _DataPart {
   created,
   deleted,
   delta,
+  persisted,
 }
 
 enum _SizeType {
@@ -84,8 +89,9 @@
   }
 }
 
-class _InstanceColumn extends ColumnData<DiffClassStats> {
-  _InstanceColumn(this.dataPart)
+class _InstanceColumn extends ColumnData<DiffClassStats>
+    implements ColumnRenderer<DiffClassStats> {
+  _InstanceColumn(this.dataPart, this.heap)
       : super(
           columnTitle(dataPart),
           fixedWidthPx: scaleByFontFactor(80.0),
@@ -94,6 +100,8 @@
 
   final _DataPart dataPart;
 
+  final AdaptedHeapData? heap;
+
   static String columnTitle(_DataPart dataPart) {
     switch (dataPart) {
       case _DataPart.created:
@@ -102,18 +110,25 @@
         return 'Released';
       case _DataPart.delta:
         return 'Delta';
+      case _DataPart.persisted:
+        return 'Persisted';
     }
   }
 
   @override
-  int getValue(DiffClassStats classStats) {
+  int getValue(DiffClassStats classStats) =>
+      _instances(classStats).instanceCount;
+
+  ObjectSetStats _instances(DiffClassStats classStats) {
     switch (dataPart) {
       case _DataPart.created:
-        return classStats.total.created.instanceCount;
+        return classStats.total.created;
       case _DataPart.deleted:
-        return classStats.total.deleted.instanceCount;
+        return classStats.total.deleted;
       case _DataPart.delta:
-        return classStats.total.delta.instanceCount;
+        return classStats.total.delta;
+      case _DataPart.persisted:
+        return classStats.total.persisted;
     }
   }
 
@@ -127,6 +142,38 @@
 
   @override
   bool get numeric => true;
+
+  @override
+  Widget? build(
+    BuildContext context,
+    DiffClassStats data, {
+    bool isRowSelected = false,
+    VoidCallback? onPressed,
+  }) {
+    if (!FeatureFlags.evalAndBrowse) return null;
+    final objects = _instances(data);
+    final theHeap = heap;
+
+    if (dataPart == _DataPart.delta) {
+      assert(theHeap == null);
+      assert(objects is! ObjectSet);
+      return null;
+    }
+
+    if (objects is! ObjectSet || theHeap == null) {
+      throw StateError(
+        'All columns except ${_DataPart.delta} should have objects and heap data available.',
+      );
+    }
+
+    return InstanceTableCell(
+      objects,
+      theHeap,
+      data.heapClass,
+      isSelected: isRowSelected,
+      gaContext: gac.MemoryAreas.snapshotDiff,
+    );
+  }
 }
 
 class _SizeColumn extends ColumnData<DiffClassStats> {
@@ -148,6 +195,8 @@
         return 'Freed';
       case _DataPart.delta:
         return 'Delta';
+      case _DataPart.persisted:
+        return 'Persisted';
     }
   }
 
@@ -162,6 +211,8 @@
             return classStats.total.deleted.shallowSize;
           case _DataPart.delta:
             return classStats.total.delta.shallowSize;
+          case _DataPart.persisted:
+            return classStats.total.persisted.shallowSize;
         }
       case _SizeType.retained:
         switch (dataPart) {
@@ -171,6 +222,8 @@
             return classStats.total.deleted.retainedSize;
           case _DataPart.delta:
             return classStats.total.delta.retainedSize;
+          case _DataPart.persisted:
+            return classStats.total.persisted.retainedSize;
         }
     }
   }
@@ -189,25 +242,35 @@
 }
 
 class _ClassesTableDiffColumns {
-  _ClassesTableDiffColumns(this.classFilterButton);
+  _ClassesTableDiffColumns(
+    this.classFilterButton, {
+    required this.before,
+    required this.after,
+  });
 
   final Widget classFilterButton;
 
   final retainedSizeDeltaColumn =
       _SizeColumn(_DataPart.delta, _SizeType.retained);
 
+  final AdaptedHeapData before;
+  final AdaptedHeapData after;
+
   late final List<ColumnData<DiffClassStats>> columnList =
       <ColumnData<DiffClassStats>>[
     _ClassNameColumn(classFilterButton),
-    _InstanceColumn(_DataPart.created),
-    _InstanceColumn(_DataPart.deleted),
-    _InstanceColumn(_DataPart.delta),
+    _InstanceColumn(_DataPart.created, after),
+    _InstanceColumn(_DataPart.deleted, before),
+    _InstanceColumn(_DataPart.delta, null),
+    _InstanceColumn(_DataPart.persisted, after),
     _SizeColumn(_DataPart.created, _SizeType.shallow),
     _SizeColumn(_DataPart.deleted, _SizeType.shallow),
     _SizeColumn(_DataPart.delta, _SizeType.shallow),
+    _SizeColumn(_DataPart.persisted, _SizeType.shallow),
     _SizeColumn(_DataPart.created, _SizeType.retained),
     _SizeColumn(_DataPart.deleted, _SizeType.retained),
     retainedSizeDeltaColumn,
+    _SizeColumn(_DataPart.persisted, _SizeType.retained),
   ];
 }
 
@@ -217,10 +280,14 @@
     required this.classes,
     required this.selection,
     required this.classFilterButton,
+    required this.before,
+    required this.after,
   }) : super(key: key);
 
   final List<DiffClassStats> classes;
   final ValueNotifier<DiffClassStats?> selection;
+  final AdaptedHeapData before;
+  final AdaptedHeapData after;
 
   static final _columnGroups = [
     ColumnGroup(
@@ -229,17 +296,17 @@
     ),
     ColumnGroup(
       title: 'Instances',
-      range: const Range(1, 4),
+      range: const Range(1, 5),
       tooltip: nonGcableInstancesColumnTooltip,
     ),
     ColumnGroup(
       title: 'Shallow Dart Size',
-      range: const Range(4, 7),
+      range: const Range(5, 9),
       tooltip: shallowSizeColumnTooltip,
     ),
     ColumnGroup(
       title: 'Retained Dart Size',
-      range: const Range(7, 10),
+      range: const Range(9, 13),
       tooltip: retainedSizeColumnTooltip,
     ),
   ];
@@ -251,7 +318,11 @@
     // We want to preserve the sorting and sort directions for ClassesTableDiff
     // no matter what the data passed to it is.
     const dataKey = 'ClassesTableDiff';
-    final columns = _ClassesTableDiffColumns(classFilterButton);
+    final columns = _ClassesTableDiffColumns(
+      classFilterButton,
+      before: before,
+      after: after,
+    );
     return FlatTable<DiffClassStats>(
       columns: columns.columnList,
       columnGroups: _columnGroups,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
index e0388f4..c4c03dc 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
@@ -15,10 +15,9 @@
 import '../../../../../shared/theme.dart';
 import '../../../../../shared/utils.dart';
 import '../../../shared/heap/heap.dart';
-import '../../../shared/primitives/instance_set_view.dart';
 import '../../../shared/primitives/simple_elements.dart';
 import '../../../shared/shared_memory_widgets.dart';
-import '../controller/sampler.dart';
+import 'instances.dart';
 
 class _ClassNameColumn extends ColumnData<SingleClassStats>
     implements
@@ -105,21 +104,12 @@
   }) {
     if (!FeatureFlags.evalAndBrowse) return null;
 
-    final theme = Theme.of(context);
-    final showMenu = isRowSelected;
-
-    return Row(
-      mainAxisAlignment: MainAxisAlignment.end,
-      children: [
-        InstanceSetButton(
-          textStyle:
-              isRowSelected ? theme.selectedTextStyle : theme.regularTextStyle,
-          count: getValue(data),
-          gaContext: gac.MemoryAreas.snapshotSingle,
-          sampleObtainer: showMenu ? HeapClassSampler(data, heap) : null,
-          showMenu: showMenu,
-        ),
-      ],
+    return InstanceTableCell(
+      data.objects,
+      heap,
+      data.heapClass,
+      isSelected: isRowSelected,
+      gaContext: gac.MemoryAreas.snapshotSingle,
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart
new file mode 100644
index 0000000..c474705
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart
@@ -0,0 +1,59 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+import '../../../../../shared/analytics/constants.dart';
+import '../../../../../shared/memory/adapted_heap_data.dart';
+import '../../../../../shared/memory/class_name.dart';
+import '../../../../../shared/theme.dart';
+import '../../../shared/heap/heap.dart';
+import '../../../shared/primitives/instance_set_button.dart';
+import '../controller/sampler.dart';
+
+/// Right aligned table cell, shat shows number of instances.
+///
+/// If the row is selected and count of instances is positive, shows ellipsis button
+/// with context menu for the instance set.
+class InstanceTableCell extends StatelessWidget {
+  InstanceTableCell(
+    ObjectSet objects,
+    AdaptedHeapData heap,
+    HeapClassName heapClass, {
+    required this.isSelected,
+    required this.gaContext,
+  })  : _showMenu = _shouldShowMenu(isSelected, objects),
+        _sampleObtainer = _shouldShowMenu(isSelected, objects)
+            ? HeapClassSampler(objects, heap, heapClass)
+            : null,
+        _count = objects.instanceCount;
+
+  static bool _shouldShowMenu(bool isSelected, ObjectSet objects) =>
+      isSelected && objects.instanceCount > 0;
+
+  final HeapClassSampler? _sampleObtainer;
+  final bool _showMenu;
+  final bool isSelected;
+  final MemoryAreas gaContext;
+  final int _count;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.end,
+      children: [
+        InstanceSetButton(
+          textStyle:
+              isSelected ? theme.selectedTextStyle : theme.regularTextStyle,
+          count: _count,
+          gaContext: gaContext,
+          sampleObtainer: _sampleObtainer,
+          showMenu: _showMenu,
+        ),
+      ],
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
index 68da9ee..fa4fd95 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
@@ -65,10 +65,14 @@
                     .data,
           );
         } else if (diffClasses != null) {
+          final diffHeapClasses =
+              controller.derived.heapClasses.value as DiffHeapClasses;
           classTable = ClassesTableDiff(
             classes: controller.derived.diffClassesToShow.value!,
             selection: controller.derived.selectedDiffClassStats,
             classFilterButton: classFilterButton,
+            before: diffHeapClasses.before,
+            after: diffHeapClasses.after,
           );
         } else {
           throw StateError('singleClasses or diffClasses should not be null.');
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/heap/heap.dart b/packages/devtools_app/lib/src/screens/memory/shared/heap/heap.dart
index bf4e31e..fdcf84d 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/heap/heap.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/heap/heap.dart
@@ -215,7 +215,12 @@
   static ObjectSet empty = ObjectSet()..seal();
 
   final objectsByCodes = <IdentityHashCode, AdaptedHeapObject>{};
-  final notCountedInRetained = <IdentityHashCode>{};
+
+  /// Subset of objects that are excluded from the retained size
+  /// calculation for this set.
+  ///
+  /// See [countInstance].
+  final objectsExcludedFromRetainedSize = <IdentityHashCode>{};
 
   @override
   bool get isZero => objectsByCodes.isEmpty;
@@ -228,7 +233,7 @@
     if (objectsByCodes.containsKey(object.code)) return;
     super.countInstance(object, excludeFromRetained: excludeFromRetained);
     objectsByCodes[object.code] = object;
-    if (excludeFromRetained) notCountedInRetained.add(object.code);
+    if (excludeFromRetained) objectsExcludedFromRetainedSize.add(object.code);
   }
 
   @override
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_view.dart b/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_button.dart
similarity index 91%
rename from packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_view.dart
rename to packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_button.dart
index aaaac1a..a79558b 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_view.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_set_button.dart
@@ -31,7 +31,8 @@
     required this.sampleObtainer,
     required this.showMenu,
     required this.gaContext,
-  }) : assert(showMenu == (sampleObtainer != null));
+  })  : assert(showMenu == (sampleObtainer != null)),
+        assert(count >= 0);
 
   final int count;
   final ClassSampler? sampleObtainer;
@@ -41,18 +42,20 @@
 
   @override
   Widget build(BuildContext context) {
+    final shouldShowMenu = showMenu && count > 0;
+
     return Row(
       children: [
         Text(
           nf.format(count),
           style: textStyle,
         ),
-        if (showMenu)
+        if (shouldShowMenu)
           ContextMenuButton(
             style: textStyle,
             menu: _menu(sampleObtainer!),
           ),
-        if (!showMenu) const SizedBox(width: ContextMenuButton.width),
+        if (!shouldShowMenu) const SizedBox(width: ContextMenuButton.width),
       ],
     );
   }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants.dart
index afe57da..178e332 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants.dart
@@ -202,6 +202,7 @@
 /// by a widget used in different contexts.
 enum MemoryAreas {
   snapshotSingle('single'),
+  snapshotDiff('diff'),
   snapshotDiffDelta('diff-delta'),
   snapshotDiffNew('diff-new'),
   ;
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index b67edcb..be3453a 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -23,6 +23,7 @@
 ## Memory updates
 * Change filter default to show only project and 3rd party dependencies [#5201](https://github.com/flutter/devtools/pull/5201).
 * Support expression evaluation in console for running application [#5248](https://github.com/flutter/devtools/pull/5248).
+* Add column `Persisted` for memory diffing [#5290](https://github.com/flutter/devtools/pull/5290).
 
 ## Debugger updates
 * Add support for browser navigation history when navigating using the `File Explorer` [#4906](https://github.com/flutter/devtools/pull/4906).
diff --git a/packages/devtools_app/test/memory/diff/controller/heap_diff_test.dart b/packages/devtools_app/test/memory/diff/controller/heap_diff_test.dart
index 817440d..9df287b 100644
--- a/packages/devtools_app/test/memory/diff/controller/heap_diff_test.dart
+++ b/packages/devtools_app/test/memory/diff/controller/heap_diff_test.dart
@@ -47,6 +47,7 @@
     expect(stats.total.created.instanceCount, 2);
     expect(stats.total.deleted.instanceCount, 1);
     expect(stats.total.delta.instanceCount, 1);
+    expect(stats.total.persisted.instanceCount, 1);
   });
 
   test('$DiffClassStats calculates deletion as expected', () async {
@@ -62,6 +63,7 @@
     expect(stats.total.created.instanceCount, 0);
     expect(stats.total.deleted.instanceCount, 1);
     expect(stats.total.delta.instanceCount, -1);
+    expect(stats.total.persisted.instanceCount, 0);
   });
 }
 
diff --git a/packages/devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart b/packages/devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart
index 208865a..f263ad4 100644
--- a/packages/devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart
+++ b/packages/devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart
@@ -35,16 +35,16 @@
 class _MyHomePageState extends State<MyHomePage> {
   final _garbage = <_MyGarbage>[];
   int _counter = 0;
-  _MyGarbage _gcableItem = _MyGarbage(0);
+  _MyGarbage _gcableItem = _MyGarbage(0, 'Should be gced, initial.');
 
   void _incrementCounter() {
     setState(() {
       _counter++;
-      _garbage.add(_MyGarbage(0));
+      _garbage.add(_MyGarbage(0, 'Never gced.'));
       if (identityHashCode(_gcableItem) < 0) {
         // We need this block to show compiler [_gcableItem] is in use.
       }
-      _gcableItem = _MyGarbage(0);
+      _gcableItem = _MyGarbage(0, 'Should be gced, initial.');
     });
   }
 
@@ -80,7 +80,7 @@
 /// Contains references of different types to
 /// test representation of a heap instance in DevTools console.
 class _MyGarbage {
-  _MyGarbage(this._level) {
+  _MyGarbage(this._level, this._note) {
     if (_level >= _depth) {
       childClass = null;
       childList = null;
@@ -88,25 +88,26 @@
       mapSimpleValue = null;
       map = null;
     } else {
-      childClass = _MyGarbage(_level + 1);
+      childClass = _MyGarbage(_level + 1, _note);
 
       childList =
-          Iterable.generate(_width, (_) => _MyGarbage(_level + 1)).toList();
+          Iterable.generate(_width, (_) => _MyGarbage(_level + 1, _note))
+              .toList();
 
       mapSimpleKey = Map.fromIterable(
         Iterable.generate(_width),
-        value: (_) => _MyGarbage(_level + 1),
+        value: (_) => _MyGarbage(_level + 1, _note),
       );
 
       mapSimpleValue = Map.fromIterable(
         Iterable.generate(_width),
-        key: (_) => _MyGarbage(_level + 1),
+        key: (_) => _MyGarbage(_level + 1, _note),
       );
 
       map = Map.fromIterable(
         Iterable.generate(_width),
-        key: (_) => _MyGarbage(_level + 1),
-        value: (_) => _MyGarbage(_level + 1),
+        key: (_) => _MyGarbage(_level + 1, _note),
+        value: (_) => _MyGarbage(_level + 1, _note),
       );
     }
   }
@@ -115,6 +116,7 @@
   static const _width = 3;
 
   final int _level;
+  final String _note;
 
   late final _MyGarbage? childClass;
   late final List<_MyGarbage>? childList;
diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_custom_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_custom_diff.png
index 50b303f..97ee8af 100644
--- a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_custom_diff.png
+++ b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_custom_diff.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png
index 27f7703..7959f26 100644
--- a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png
+++ b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_only_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_only_diff.png
index 50b303f..97ee8af 100644
--- a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_only_diff.png
+++ b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_only_diff.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png
index 99ec112..3f8dcb0 100644
--- a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png
+++ b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png
index 27f7703..7959f26 100644
--- a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png
+++ b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png
Binary files differ