devtools icon indicating copy to clipboard operation
devtools copied to clipboard

App with many Finalizers is always killed after Memory tab is opened

Open kaboc opened this issue 1 year ago • 0 comments

I noticed this issue while I was measuring the performance of my package with a benchmarking app (like a stress test) by someone else. I'm not sure if this is a bug of the DevTools, but it does not happen unless the Memory tab is opened, so I thought I should file an issue here rather than in flutter/flutter.

Steps to reproduce

  1. Run the sample app below.
  2. Open the DevTools.
  3. Switch the tab to "Memory".
  4. Wait for a while.
  5. If nothing happens, raise the value of number, and then restart the app.
  6. Comment out the line of _finalizer.attach(...); and try the same steps to see there is no issue then.

Code sample

This sample shows and hides a large number of empty widgets at very short intervals. The callbacks of finalizers are triggered after the widgets are removed and their BuildContexts become unreachable.

import 'dart:async';
import 'package:flutter/material.dart';

const number = 1000;
const interval = Duration(milliseconds: 8);

void main() => runApp(App());

final Finalizer<void> _finalizer = Finalizer((_) {/* no-op */});

class App extends StatefulWidget {
  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  Timer? _timer;
  bool _shown = false;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(interval, (_) {
      setState(() => _shown = !_shown);
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            Text(_shown ? 'Shown' : 'Hidden'),
            if (_shown)
              for (var i = 0; i < number; i++) _Widget(),
          ],
        ),
      ),
    );
  }
}

class _Widget extends StatefulWidget {
  @override
  State<_Widget> createState() => _WidgetState();
}

class _WidgetState extends State<_Widget> {
  @override
  void initState() {
    super.initState();
    _finalizer.attach(context, null);
  }

  @override
  Widget build(BuildContext context) {
    return const SizedBox.shrink();
  }
}

Logs

Launching lib\main.dart on Windows in debug mode...
Building Windows application...
√  Built build\windows\x64\runner\Debug\devtools_bug.exe.
Debug service listening on ws://127.0.0.1:64017/g28TCr5ZPiw=/ws
Syncing files to device Windows...
../../third_party/dart/runtime/vm/object_service.cc: 2069: error: unreachable code
version=3.2.6 (stable) (Wed Jan 24 13:41:58 2024 +0000) on "windows_x64"
pid=15592, thread=21096, isolate_group=main(000001FA7458C8D0), isolate=main(000001FA745924B0)
os=windows, arch=x64, comp=no, sim=no
isolate_instructions=7ffd0028b940, vm_instructions=7ffd0028b950
fp=dffd7fd750, sp=dffd7fd750, pc=7ffd003ba992
  pc 0x00007ffd003ba992 fp 0x000000dffd7fd750 FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable+0x8272f2
-- End of DumpStackTrace
  pc 0x0000000000000000 fp 0x000000dffd7fef90 sp 0x0000000000000000 [Stub] CallToRuntime
  pc 0x000001fa04609dc6 fp 0x000000dffd7ff030 sp 0x000000dffd7fefa0 [Optimized] _FinalizerImpl@0150898._runFinalizers@0150898
  pc 0x000001fa0001f2a1 fp 0x000000dffd7ff060 sp 0x000000dffd7ff040 [Unoptimized] _FinalizerImpl@0150898._handleFinalizerMessage@0150898
  pc 0x000001fa73d02bcf fp 0x000000dffd7ff180 sp 0x000000dffd7ff070 [Stub] InvokeDartCode
Lost connection to device.

Environment

Click to open
Flutter (Channel stable, 3.16.9, on Microsoft Windows [Version 10.0.22631.3007], locale ja-JP)
• Flutter version 3.16.9 on channel stable at D:\xxxx\flutter\sdk
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 41456452f2 (2 weeks ago), 2024-01-25 10:06:23 -0800
• Engine revision f40e976bed
• Dart version 3.2.6
• DevTools version 2.28.5

kaboc avatar Feb 09 '24 07:02 kaboc