thunderbird / thunderbird/thunderbird-android

OutOfMemoryError loading large message list / combined inbox (legacy whole-list loader)

Open
#11,069 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
14k
Forks
2.8k
Avg merge
3d 3h
Merged PRs (30d)
57

Description

Checklist
  • I have used the search function to see if someone else has already submitted the same bug report.
  • I will describe the problem with as much detail as possible.
App

Thunderbird for Android

App version

19.0 (versionCode 23)

Where did you get the app from?

Google Play Store

Android version

Android 16 (One UI 8.0, build BP2A.250605.031.A3.F946BXXS6FZC5)

Device model

Samsung Galaxy Z Fold5 (SM-F946B)

Steps to reproduce
  1. Configure an account (or use the combined/unified inbox) backed by a large folder (tens of thousands of messages).
  2. Open that message list / the combined inbox and let it load.
  3. The app fails to allocate and crashes with OutOfMemoryError.
Expected behavior

The message list opens without exhausting the heap, regardless of folder size.

Actual behavior

The app crashes with java.lang.OutOfMemoryError. The heap is essentially full at the point of failure: the allocation that fails is tiny (32 bytes) while GC reports <1% of the 512 MB heap free. So the crash is a symptom of retained memory, not of the allocation site itself.

Root-cause analysis (not just the trace):

The legacy message list loads the entire matching message set into a single in-memory List<MessageListItem> before display, with no windowing/paging:

  • MessageListLoader.getMessageListInfo does accounts.flatMap { loadMessageListForAccount(...) }.sortedWith(...) (legacy/ui/legacy/.../ui/messagelist/MessageListLoader.kt). For a combined inbox this is multiplied across every account.
  • Each row becomes a MessageListItem retaining several strings (subject, preview text, display name, formatted date, UID). At tens of thousands of rows this retained set fills the heap.

The RelativeDateTimeFormatter.formatDate frame at the top of the stack is incidental: it is simply the first allocation to fail once the heap is already saturated by the retained list. It is not the leak.

This looks like the exact condition that the roadmap project #8838 ("Refactor: Message List") was created to fix: "limit the amount of memory used to display the message list… by only loading information about a limited number of messages into memory at a time… the window… adjusted when the user scrolls." The windowed/Compose rewrite exists under feature/mail/message/list/* but is currently gated behind the UseComposeForMessageListItems and EnableMessageListNewState feature flags, both disabled in release builds, so shipping users still hit the legacy whole-list loader described above.

Likely a duplicate/sibling of #10556 (constant crashes, combined mail, large mailbox).

Question for maintainers: is an interim guard wanted on the legacy loader (e.g. a load cap / visible-window limit) until the windowed rewrite is enabled by default, or should this be tracked purely under #8838?

Logs
java.lang.OutOfMemoryError: Failed to allocate a 32 byte allocation with 5300704 free bytes and 5176KB until OOM, target footprint 536870912, growth limit 536870912; giving up on allocation because <1% of heap free after GC.
	at java.lang.StringFactory.newStringFromUtf8Bytes(Native Method)
	at java.lang.StringFactory.newStringFromBytes(StringFactory.java:94)
	at java.lang.StringFactory.newStringFromBytes(StringFactory.java:68)
	at java.lang.StringFactory.newStringFromBytes(StringFactory.java:44)
	at java.lang.Long.toString(Long.java:480)
	at android.icu.impl.OlsonTimeZone.toString(OlsonTimeZone.java:794)
	at java.lang.String.valueOf(String.java:4788)
	at java.lang.StringBuilder.append(StringBuilder.java:183)
	at android.text.format.DateIntervalFormat.getFormatter(DateIntervalFormat.java:111)
	at android.text.format.DateIntervalFormat.formatDateRange(DateIntervalFormat.java:103)
	at android.text.format.DateIntervalFormat.formatDateRange(DateIntervalFormat.java:62)
	at android.text.format.DateUtils.formatDateRange(DateUtils.java:834)
	at android.text.format.DateUtils.formatDateRange(DateUtils.java:659)
	at android.text.format.DateUtils.formatDateRange(DateUtils.java:636)
	at com.fsck.k9.ui.helper.RelativeDateTimeFormatter.formatDate(RelativeDateTimeFormatter)
	at com.fsck.k9.storage.messages.RetrieveMessageListOperations...doDbWork
	at com.fsck.k9.mailstore.LockableDatabase.execute
	at com.fsck.k9.mailstore.DefaultMessageListRepository.getThreadedMessages
	at com.fsck.k9.ui.messagelist.MessageListLoader.getMessageListInfo
	at com.fsck.k9.ui.messagelist.MessageListLiveData$loadMessageListAsync$1$messageList$1.invokeSuspend
	... (coroutine dispatch frames)

The original trace is R8-obfuscated (r8-map-id-…), so some adjacent frames are merged/synthetic; the symbolicated path above reflects the real call chain through MessageListLoaderDefaultMessageListRepository.getThreadedMessagesRetrieveMessageListOperations.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with legacy/ui/legacy/.../ui/messagelist/MessageListLoader.kt and trace getMessageListInfo through DefaultMessageListRepository and RetrieveMessageListOperations. Compare that path with feature/mail/message/list/* and inspect the UseComposeForMessageListItems and EnableMessageListNewState flags. Done means an agreed implementation prevents large or combined message lists from exhausting memory, with maintainers deciding whether this belongs under #8838.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile-dev, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.