Skip to content

Commit 547bfe7

Browse files
Dispose all disposables in the Memory screen (#8953)
1 parent 2ad3968 commit 547bfe7

15 files changed

Lines changed: 190 additions & 78 deletions

File tree

packages/devtools_app/lib/src/screens/memory/framework/memory_controller.dart

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,22 @@ import '../panes/profile/profile_pane_controller.dart';
2323
import '../panes/tracing/tracing_pane_controller.dart';
2424
import 'offline_data/offline_data.dart';
2525

26-
/// This class contains the business logic for memory screen.
26+
/// Screen controller for the Memory screen.
27+
///
28+
/// This controller can be accessed from anywhere in DevTools, as long as it was
29+
/// first registered, by calling `screenControllers.lookup<MemoryController>()`.
30+
///
31+
/// The controller lifecycle is managed by the [ScreenControllers] class. The
32+
/// `init` method is called lazily upon the first controller access from
33+
/// `screenControllers`. The `dispose` method is called by `screenControllers`
34+
/// when DevTools is destroying a set of DevTools screen controllers.
2735
///
2836
/// This class must not have direct dependencies on web-only libraries. This
2937
/// allows tests of the complicated logic in this class to run on the VM.
30-
///
31-
/// The controller should be recreated for every new connection.
3238
class MemoryController extends DevToolsScreenController
3339
with
3440
AutoDisposeControllerMixin,
3541
OfflineScreenControllerMixin<OfflineMemoryData> {
36-
MemoryController({
37-
@visibleForTesting DiffPaneController? connectedDiff,
38-
@visibleForTesting ProfilePaneController? connectedProfile,
39-
}) {
40-
unawaited(
41-
_init(connectedDiff: connectedDiff, connectedProfile: connectedProfile),
42-
);
43-
}
44-
4542
Future<void> get initialized => _initialized.future;
4643
final _initialized = Completer<void>();
4744

@@ -61,14 +58,26 @@ class MemoryController extends DevToolsScreenController
6158

6259
late final TracePaneController? trace;
6360

61+
@override
62+
void init({
63+
@visibleForTesting DiffPaneController? connectedDiff,
64+
@visibleForTesting ProfilePaneController? connectedProfile,
65+
}) {
66+
super.init();
67+
unawaited(
68+
_init(connectedDiff: connectedDiff, connectedProfile: connectedProfile),
69+
);
70+
}
71+
6472
@override
6573
void dispose() {
66-
super.dispose();
6774
HeapClassName.dispose();
68-
chart.dispose();
69-
trace?.dispose();
7075
diff.dispose();
7176
profile?.dispose();
77+
chart.dispose();
78+
trace?.dispose();
79+
_gcing.dispose();
80+
super.dispose();
7281
}
7382

7483
static const _dataKey = 'data';
@@ -77,7 +86,7 @@ class MemoryController extends DevToolsScreenController
7786
@visibleForTesting DiffPaneController? connectedDiff,
7887
@visibleForTesting ProfilePaneController? connectedProfile,
7988
}) async {
80-
assert(!_initialized.isCompleted);
89+
if (_initialized.isCompleted) return;
8190
if (offlineDataController.showingOfflineData.value) {
8291
assert(connectedDiff == null && connectedProfile == null);
8392
await maybeLoadOfflineData(

packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_connection.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class ChartVmConnection extends DisposableController
112112
_polling?.cancel();
113113
_polling?.dispose();
114114
_polling = null;
115+
_memoryTracker.dispose();
115116
super.dispose();
116117
}
117118
}

packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_data.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ class ChartData with Serializable {
7272
void dispose() {
7373
_displayInterval.dispose();
7474
_isLegendVisible.dispose();
75+
timeline.dispose();
7576
}
7677
}

packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_pane_controller.dart

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ import 'charts/vm_chart_controller.dart';
1515
class MemoryChartPaneController extends DisposableController
1616
with AutoDisposeControllerMixin {
1717
MemoryChartPaneController({required this.data}) {
18+
init();
19+
}
20+
21+
late final ChartData data;
22+
23+
ChartVmConnection? _chartConnection;
24+
25+
late final event = EventChartController(data.timeline, paused: paused);
26+
late final vm = VMChartController(data.timeline, paused: paused);
27+
late final android = AndroidChartController(
28+
data.timeline,
29+
sharedLabels: vm.labelTimestamps,
30+
paused: paused,
31+
);
32+
33+
@override
34+
void init() {
35+
super.init();
1836
if (offlineDataController.showingOfflineData.value) {
1937
// Setting paused to false, because `recomputeChartData` is noop when it is true.
2038
_paused.value = false;
@@ -32,18 +50,6 @@ class MemoryChartPaneController extends DisposableController
3250
);
3351
}
3452

35-
late final ChartData data;
36-
37-
ChartVmConnection? _chartConnection;
38-
39-
late final event = EventChartController(data.timeline, paused: paused);
40-
late final vm = VMChartController(data.timeline, paused: paused);
41-
late final android = AndroidChartController(
42-
data.timeline,
43-
sharedLabels: vm.labelTimestamps,
44-
paused: paused,
45-
);
46-
4753
void resetAll() {
4854
event.reset();
4955
vm.reset();
@@ -101,12 +107,13 @@ class MemoryChartPaneController extends DisposableController
101107

102108
@override
103109
void dispose() {
104-
super.dispose();
105110
data.dispose();
106111
event.dispose();
107112
vm.dispose();
108113
android.dispose();
114+
_paused.dispose();
109115
isAndroidChartVisible.dispose();
110116
_chartConnection?.dispose();
117+
super.dispose();
111118
}
112119
}

packages/devtools_app/lib/src/screens/memory/panes/chart/controller/memory_tracker.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66
import 'dart:math' as math;
77

88
import 'package:devtools_app_shared/service.dart' show FlutterEvent;
9+
import 'package:devtools_app_shared/utils.dart';
910
import 'package:devtools_shared/devtools_shared.dart';
1011
import 'package:flutter/foundation.dart';
1112
import 'package:logging/logging.dart';
@@ -20,7 +21,7 @@ final _log = Logger('memory_protocol');
2021

2122
enum _ContinuesState { none, stop, next }
2223

23-
class MemoryTracker {
24+
class MemoryTracker extends Disposable {
2425
MemoryTracker(this.timeline, {required this.isAndroidChartVisible});
2526

2627
final MemoryTimeline timeline;
@@ -291,4 +292,11 @@ class MemoryTracker {
291292

292293
return null;
293294
}
295+
296+
@override
297+
void dispose() {
298+
_monitorContinues?.cancel();
299+
_monitorContinues = null;
300+
super.dispose();
301+
}
294302
}

packages/devtools_app/lib/src/screens/memory/panes/diff/controller/class_data.dart

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
44

5+
import 'package:devtools_app_shared/utils.dart';
56
import 'package:flutter/foundation.dart';
67

78
import '../../../../../shared/memory/classes.dart';
@@ -11,12 +12,19 @@ import '../../../shared/heap/class_filter.dart';
1112
import '../../../shared/primitives/simple_elements.dart';
1213
import '../data/classes_diff.dart';
1314

14-
class RetainingPathController {
15+
class RetainingPathController extends Disposable {
1516
final hideStandard = ValueNotifier<bool>(true);
1617
final invert = ValueNotifier<bool>(true);
18+
19+
@override
20+
void dispose() {
21+
hideStandard.dispose();
22+
invert.dispose();
23+
super.dispose();
24+
}
1725
}
1826

19-
class ClassesTableSingleData {
27+
class ClassesTableSingleData extends Disposable {
2028
ClassesTableSingleData({
2129
required this.heap,
2230
required this.totalHeapSize,
@@ -37,9 +45,15 @@ class ClassesTableSingleData {
3745

3846
/// Selected class.
3947
final selection = ValueNotifier<SingleClassData?>(null);
48+
49+
@override
50+
void dispose() {
51+
selection.dispose();
52+
super.dispose();
53+
}
4054
}
4155

42-
class ClassesTableDiffData {
56+
class ClassesTableDiffData extends Disposable {
4357
ClassesTableDiffData({
4458
required this.heapBefore,
4559
required this.heapAfter,
@@ -63,6 +77,13 @@ class ClassesTableDiffData {
6377

6478
/// Selected class.
6579
final selection = ValueNotifier<DiffClassData?>(null);
80+
81+
@override
82+
void dispose() {
83+
selectedSizeType.dispose();
84+
selection.dispose();
85+
super.dispose();
86+
}
6687
}
6788

6889
/// Data for visualization of a path.

packages/devtools_app/lib/src/screens/memory/panes/diff/controller/diff_pane_controller.dart

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,21 @@ class DiffPaneController extends DisposableController with Serializable {
220220
),
221221
);
222222
}
223+
224+
@override
225+
void dispose() {
226+
retainingPathController.dispose();
227+
core.dispose();
228+
derived.dispose();
229+
super.dispose();
230+
}
223231
}
224232

225233
/// Values that define what data to show on diff screen.
226234
///
227235
/// Widgets should not update the fields directly, they should use
228236
/// [DiffPaneController] or [DerivedData] for this.
229-
class CoreData {
237+
class CoreData extends Disposable {
230238
CoreData(this.rootPackage);
231239

232240
final String? rootPackage;
@@ -263,44 +271,21 @@ class CoreData {
263271
/// This filter is applied to all snapshots.
264272
ValueListenable<ClassFilter> get classFilter => _classFilter;
265273
final _classFilter = ValueNotifier(ClassFilter.theDefault());
274+
275+
@override
276+
void dispose() {
277+
_snapshots.dispose();
278+
_selectedSnapshotIndex.dispose();
279+
_classFilter.dispose();
280+
super.dispose();
281+
}
266282
}
267283

268284
/// Values that can be calculated from [CoreData] and notifiers that take signal
269285
/// from widgets.
270286
class DerivedData extends DisposableController with AutoDisposeControllerMixin {
271287
DerivedData(this._core) {
272-
_selectedItem = ValueNotifier<SnapshotItem>(_core.selectedItem);
273-
274-
final classFilterData = ClassFilterData(
275-
filter: _core.classFilter,
276-
onChanged: applyFilter,
277-
rootPackage: _core.rootPackage,
278-
);
279-
280-
classesTableSingle = ClassesTableSingleData(
281-
heap: () => (_core.selectedItem as SnapshotDataItem).heap!,
282-
filterData: classFilterData,
283-
totalHeapSize: () => (_core.selectedItem as SnapshotDataItem).totalSize!,
284-
);
285-
286-
classesTableDiff = ClassesTableDiffData(
287-
heapBefore: () => _currentDiff()!.before,
288-
heapAfter: () => _currentDiff()!.after,
289-
filterData: classFilterData,
290-
);
291-
292-
addAutoDisposeListener(
293-
classesTableSingle.selection,
294-
() => _setClassIfNotNull(classesTableSingle.selection.value?.className),
295-
);
296-
addAutoDisposeListener(
297-
classesTableDiff.selection,
298-
() => _setClassIfNotNull(classesTableDiff.selection.value?.className),
299-
);
300-
addAutoDisposeListener(
301-
selectedPath,
302-
() => _setPathIfNotNull(selectedPath.value?.path),
303-
);
288+
init();
304289
}
305290

306291
final CoreData _core;
@@ -337,6 +322,56 @@ class DerivedData extends DisposableController with AutoDisposeControllerMixin {
337322
/// Storage for already calculated diffs between snapshots.
338323
final _diffStore = HeapDiffStore();
339324

325+
@override
326+
void init() {
327+
super.init();
328+
_selectedItem = ValueNotifier<SnapshotItem>(_core.selectedItem);
329+
330+
final classFilterData = ClassFilterData(
331+
filter: _core.classFilter,
332+
onChanged: applyFilter,
333+
rootPackage: _core.rootPackage,
334+
);
335+
336+
classesTableSingle = ClassesTableSingleData(
337+
heap: () => (_core.selectedItem as SnapshotDataItem).heap!,
338+
filterData: classFilterData,
339+
totalHeapSize: () => (_core.selectedItem as SnapshotDataItem).totalSize!,
340+
);
341+
342+
classesTableDiff = ClassesTableDiffData(
343+
heapBefore: () => _currentDiff()!.before,
344+
heapAfter: () => _currentDiff()!.after,
345+
filterData: classFilterData,
346+
);
347+
348+
addAutoDisposeListener(
349+
classesTableSingle.selection,
350+
() => _setClassIfNotNull(classesTableSingle.selection.value?.className),
351+
);
352+
addAutoDisposeListener(
353+
classesTableDiff.selection,
354+
() => _setClassIfNotNull(classesTableDiff.selection.value?.className),
355+
);
356+
addAutoDisposeListener(
357+
selectedPath,
358+
() => _setPathIfNotNull(selectedPath.value?.path),
359+
);
360+
}
361+
362+
@override
363+
void dispose() {
364+
_selectedItem.dispose();
365+
classesBeforeFiltering.dispose();
366+
_singleClassesToShow.dispose();
367+
_diffClassesToShow.dispose();
368+
classData.dispose();
369+
selectedPath.dispose();
370+
classesTableSingle.dispose();
371+
classesTableDiff.dispose();
372+
super.dispose();
373+
}
374+
340375
void applyFilter(ClassFilter filter) {
341376
if (filter == _core.classFilter.value) return;
342377
_core._classFilter.value = filter;

packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ class ProfilePaneController extends DisposableController
4848
bool _initialized = false;
4949

5050
/// Initializes the controller if it is not initialized yet.
51-
void initialize() {
51+
@override
52+
void init() {
53+
super.init();
5254
if (_initialized) return;
5355

5456
if (!offlineDataController.showingOfflineData.value) {
@@ -202,4 +204,13 @@ class ProfilePaneController extends DisposableController
202204
type: ExportFileType.csv,
203205
);
204206
}
207+
208+
@override
209+
void dispose() {
210+
_currentAllocationProfile.dispose();
211+
_classFilter.dispose();
212+
_refreshOnGc.dispose();
213+
selection.dispose();
214+
super.dispose();
215+
}
205216
}

0 commit comments

Comments
 (0)