Skip to content

[Android] GIF image loading causes file descriptor leak (FD count grows indefinitely) only when images are visible on screen #186899

@m7yun

Description

@m7yun

Steps to reproduce

When loading and displaying GIF images on Android, the number of open file descriptors (FD) increases indefinitely and never gets released, eventually causing the app to become unresponsive or crash due to FD exhaustion.

Important finding: The FD leak only occurs when GIF images are actually visible on screen. When GIFs are scrolled off-screen or the widget is unmounted, the FD count does NOT increase. This suggests the leak is related to active decoding/rendering of GIF frames, not just caching or loading.

This issue started occurring in Flutter 3.41.x and persists in 3.44.0. The same code works perfectly in Flutter 3.38.10.

1.Create a simple Flutter app that loads GIF images (e.g., using Image.network or Image.asset)

2.Keep the GIFs visible on screen (e.g., in a ListView without scrolling, or a PageView)

3.Monitor FD count using /proc/self/fd

4.Observe FD count continuously increasing while GIFs are visible

5.Scroll the GIFs off-screen or navigate away — FD growth stops

Expected results

FD count should remain stable regardless of whether GIFs are visible on screen.

Actual results

GIFs visible on screen → FD count grows indefinitely

GIFs not visible (scrolled off-screen / disposed) → FD count remains stable

Eventually FD limit is reached (typically 1024-4096), causing app to slow down and crash

Code sample

Code sample
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('GIF FD Leak Test')),
        body: ListView.builder(
          itemCount: 20,
          itemBuilder: (context, index) => Image.network(
            'https://example.com/animated.gif', // Any GIF URL
            height: 300,
          ),
        ),
      ),
    );
  }
}

FD Monitoring Code (Kotlin)
Add this to MyApplication.kt:

Code sample
Handler(Looper.getMainLooper()).post(object : Runnable {
    override fun run() {
        val fdCount = File("/proc/self/fd").listFiles()?.size ?: 0
        Log.d("FD_Monitor", "Current FD count: $fdCount")
        Handler(Looper.getMainLooper()).postDelayed(this, 2000)
    }
})

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

// With GIFs VISIBLE on screen:

Logs
D/FD_Monitor: Current FD count: 247
D/FD_Monitor: Current FD count: 312
D/FD_Monitor: Current FD count: 389
D/FD_Monitor: Current FD count: 451
D/FD_Monitor: Current FD count: 523
D/FD_Monitor: Current FD count: 598
... (continues growing until crash)

// After scrolling GIFs OFF screen:

Logs
D/FD_Monitor: Current FD count: 598
D/FD_Monitor: Current FD count: 599
D/FD_Monitor: Current FD count: 598
D/FD_Monitor: Current FD count: 597
... (stable, no further growth)

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.41.6)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode (Version 15.4)
[✓] VS Code (version 1.89.1)
[✓] Connected device (1 available)

Metadata

Metadata

Assignees

Labels

needs repro infoAutomated crash report whose cause isn't yet known

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions