Skip to content

Memory leak caused by static ThreadLocal in DateFormatUtils #122

@QiuYucheng2003

Description

@QiuYucheng2003

Describe the issue

I identified a resource retention / memory leak issue in the Android implementation of the plugin (com.squareup.sdk.reader.flutter.internal.DateFormatUtils).

The class uses a private static final ThreadLocal to cache SimpleDateFormat instances. However, there is no mechanism to invoke remove() on this ThreadLocal.

On Android, where threads (e.g., from the standard thread pool or main looper) often outlive the specific Flutter Engine or Activity lifecycle, holding these instances statically prevents the ClassLoader (and potentially associated resources) from being garbage collected. This effectively creates a memory leak that accumulates every time the class is loaded in a new context (e.g. Flutter engine restart).

Source Code Location:
// android/src/main/java/com/squareup/sdk/reader/flutter/internal/DateFormatUtils.java

private static final ThreadLocal ISO_8601 = new ThreadLocal() {
@OverRide protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
}
};
// .remove() is never called.

To Reproduce

  1. Initialize the Square Reader SDK in a Flutter Android app.

  2. Trigger functionality that invokes DateFormatUtils.formatISO8601UTC (populating the ThreadLocalMap of the current thread).

  3. Detach/Destroy the Flutter Engine (or recreate the Activity/Context multiple times).

  4. Observation: Inspect the Heap Dump. You will see SimpleDateFormat instances referenced by ThreadLocalMap entries of alive threads, preventing proper GC of the plugin's classes.

Expected behavior

The utility class should avoid using static ThreadLocal for SimpleDateFormat. On modern Android (ART), the cost of creating a new SimpleDateFormat is negligible compared to the risk of memory leaks. The cache should be removed, or a cleanup mechanism must be implemented.

Suggested Fix
Replace the ThreadLocal cache with a local variable instantiation.
// Recommended change in DateFormatUtils.java
public static String formatISO8601UTC(Date date) {
// Thread-safe, no leak risk
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions