[framework] dont allocate forgotten children set in profile/release mode (#94911)

diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart
index 5e77fe7..68dae54 100644
--- a/dev/bots/analyze.dart
+++ b/dev/bots/analyze.dart
@@ -98,6 +98,9 @@
   print('$clock Integration test timeouts...');
   await verifyIntegrationTestTimeouts(flutterRoot);
 
+  print('$clock null initialized debug fields...');
+  await verifyNullInitializedDebugExpensiveFields(flutterRoot);
+
   // Ensure that all package dependencies are in sync.
   print('$clock Package dependencies...');
   await runCommand(flutter, <String>['update-packages', '--verify-only'],
@@ -785,7 +788,7 @@
   final Set<String> suggestions = <String>{};
   final List<File> files = await _gitFiles(workingDirectory);
   for (final File file in files) {
-    if (path.basename(file.path).endsWith('_test.dart'))
+    if (path.basename(file.path).endsWith('_test.dart') || path.basename(file.path) == 'analyze.dart')
       continue; // Skip tests, they're not public-facing.
     final Uint8List bytes = file.readAsBytesSync();
     // We allow invalid UTF-8 here so that binaries don't trip us up.
@@ -1492,6 +1495,40 @@
   }
 }
 
+const String _kDebugOnlyAnnotation = '@_debugOnly';
+final RegExp _nullInitializedField = RegExp(r'kDebugMode \? [\w\<\> ,{}()]+ : null;');
+
+Future<void> verifyNullInitializedDebugExpensiveFields(String workingDirectory, {int minimumMatches = 400}) async {
+  final String flutterLib = path.join(workingDirectory, 'packages', 'flutter', 'lib');
+  final List<File> files = await _allFiles(flutterLib, 'dart', minimumMatches: minimumMatches)
+    .toList();
+  final List<String> errors = <String>[];
+  for (final File file in files) {
+    final List<String> lines = file.readAsLinesSync();
+    for (int i = 0; i < lines.length; i += 1) {
+      final String line = lines[i];
+      if (!line.contains(_kDebugOnlyAnnotation)) {
+        continue;
+      }
+      final String nextLine = lines[i + 1];
+      if (_nullInitializedField.firstMatch(nextLine) == null) {
+        errors.add('${file.path} L$i');
+      }
+    }
+  }
+
+  if (errors.isNotEmpty) {
+    exitWithError(<String>[
+     '${bold}ERROR: ${red}fields annotated with @_debugOnly must null initialize.$reset',
+     'to ensure both the field and initializer are removed from profile/release mode.',
+     'These fields should be written as:\n',
+     'field = kDebugMode ? <DebugValue> : null;\n',
+     'Errors were found in the following files:',
+      ...errors,
+    ]);
+  }
+}
+
 Future<CommandResult> _runFlutterAnalyze(String workingDirectory, {
   List<String> options = const <String>[],
 }) async {
diff --git a/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart b/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart
new file mode 100644
index 0000000..52fe689
--- /dev/null
+++ b/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart
@@ -0,0 +1,18 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+class _DebugOnly {
+  const _DebugOnly();
+}
+
+const _DebugOnly _debugOnly = _DebugOnly();
+const bool kDebugMode = bool.fromEnvironment('test-only');
+
+class Foo {
+  @_debugOnly
+  final Map<String, String>? foo = kDebugMode ? <String, String>{} : null;
+
+  @_debugOnly
+  final Map<String, String>? bar = kDebugMode ? null : <String, String>{};
+}
diff --git a/dev/bots/test/analyze_test.dart b/dev/bots/test/analyze_test.dart
index 5ba5ff4..d554895 100644
--- a/dev/bots/test/analyze_test.dart
+++ b/dev/bots/test/analyze_test.dart
@@ -193,4 +193,14 @@
       },
     ));
   });
+
+  test('analyze.dart - verifyNullInitializedDebugExpensiveFields', () async {
+    final String result = await capture(() => verifyNullInitializedDebugExpensiveFields(
+      testRootPath,
+      minimumMatches: 1,
+    ), exitCode: 1);
+
+    expect(result, contains('L15'));
+    expect(result, isNot(contains('L12')));
+  });
 }