Version
Media3 main branch
More version details
No response
Devices that reproduce the issue
Samsung running Android 13 (RAM free: 870.45 MB Disk free: 12.37 GB)
Oppo running Android 14 (RAM free: 1.96 GB Disk free: 13.85 GB)
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
Not able to reproduce as this is reported via crashlytics.
Expected result
Should not get OOM
Actual result
Getting OOM exception while initialising simplecache for download manager
Media
Issue
Need help to fix this or any suggestion which can help here to fix SimpleCache outofmemory issue.
I also tried the LeastRecentlyUsedCacheEvictor but by that way offline videos were not getting played when maxBytes gets exceeded.
I am not able to reproduce this issue in my device, got this issue from crashlytics.
Attaching the stack trace for the crash below:
Fatal Exception: java.lang.OutOfMemoryError: Failed to allocate a 120 byte allocation with 1468512 free bytes and 1434KB until OOM, target footprint 536870912, growth limit 536870912; giving up on allocation because <1% of heap free after GC.
at java.io.UnixFileSystem.resolve(UnixFileSystem.java:146)
at java.io.File.<init>(File.java:273)
at java.io.File.listFiles(File.java:1235)
at com.google.android.exoplayer2.upstream.cache.SimpleCache.loadDirectory(SimpleCache.java:633)
at com.google.android.exoplayer2.upstream.cache.SimpleCache.initialize(SimpleCache.java:586)
at com.google.android.exoplayer2.upstream.cache.SimpleCache.access$000(SimpleCache.java:49)
at com.google.android.exoplayer2.upstream.cache.SimpleCache$1.run(SimpleCache.java:268)
Attaching the code for creating simple cache object:
val cookieManager = CookieManager()
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER)
CookieHandler.setDefault(cookieManager)
val standaloneDatabaseProvider = StandaloneDatabaseProvider(context)
val downloadContentDirectory = File(context.filesDir.absolutePath + "/" + APP_NAME + "/Videos")
val simpleCache = SimpleCache(
downloadContentDirectory, NoOpCacheEvictor(), standaloneDatabaseProvider
)
val upstreamFactory =
DefaultDataSource.Factory(context, dataSourceFactory)
cacheDataStoreFactory = CacheDataSource.Factory()
.setCache(simpleCache)
.setUpstreamDataSourceFactory(upstreamFactory)
.setCacheWriteDataSinkFactory(null)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
downloadManager = DownloadManager(
context,
standaloneDatabaseProvider,
simpleCache,
ResolvingDataSource.Factory(dataSourceFactory, resolver),
Executors.newFixedThreadPool(12)
)
I also added non-fatal logging on our side to understand the scale at which users are hitting this crash.
From production logs:
Users who crash have ~150 downloaded videos on average
Average video size ~180 MB
Total cached data is typically 25–30 GB
While investigating further, I looked into SimpleCache initialization and noticed a potential performance/memory concern in the recursive directory scan that eagerly loads all cache spans into memory:
private void loadDirectory(
File directory,
boolean isRoot,
@Nullable File[] files,
@Nullable Map<String, CacheFileMetadata> fileMetadata
) {
if (files == null || files.length == 0) {
if (!isRoot) {
directory.delete();
}
return;
}
for (File file : files) {
String fileName = file.getName();
if (isRoot && fileName.indexOf('.') == -1) {
loadDirectory(
file,
/* isRoot= */ false,
file.listFiles(),
fileMetadata
);
continue;
}
if (isRoot
&& (CachedContentIndex.isIndexFile(fileName)
|| fileName.endsWith(UID_FILE_SUFFIX))) {
continue;
}
long length = C.LENGTH_UNSET;
long lastTouchTimestamp = C.TIME_UNSET;
@Nullable CacheFileMetadata metadata =
fileMetadata != null ? fileMetadata.remove(fileName) : null;
if (metadata != null) {
length = metadata.length;
lastTouchTimestamp = metadata.lastTouchTimestamp;
}
@Nullable SimpleCacheSpan span =
SimpleCacheSpan.createCacheEntry(
file,
length,
lastTouchTimestamp,
contentIndex
);
if (span != null) {
addSpan(span);
} else {
file.delete();
}
}
}
From what I understand, this:
Recursively scans the entire cache directory at startup
Creates all SimpleCacheSpans eagerly
Populates contentIndex fully before playback
With large offline libraries, this seems like it could cause memory pressure or long blocking work during app startup.
Questions
Is eager loading of all cache spans during SimpleCache initialization mandatory, or is there a supported way to:
Lazily load cache entries, or
Defer span creation until content is actually requested?
Is it required to maintain a full cache index for all downloaded videos upfront?
Would it be valid/supported to create or prepare cache only when a user attempts to play a video, instead of indexing the entire cache at startup?
Are there recommended best practices for handling large offline libraries (100+ videos) with Media3 to avoid startup crashes or OOMs?
e.g., cache size limits, multiple caches, or alternative cache strategies.
Bug Report
Version
Media3 main branch
More version details
No response
Devices that reproduce the issue
Samsung running Android 13 (RAM free: 870.45 MB Disk free: 12.37 GB)
Oppo running Android 14 (RAM free: 1.96 GB Disk free: 13.85 GB)
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
Not able to reproduce as this is reported via crashlytics.
Expected result
Should not get OOM
Actual result
Getting OOM exception while initialising simplecache for download manager
Media
Issue
Need help to fix this or any suggestion which can help here to fix SimpleCache outofmemory issue.
I also tried the
LeastRecentlyUsedCacheEvictorbut by that way offline videos were not getting played when maxBytes gets exceeded.I am not able to reproduce this issue in my device, got this issue from crashlytics.
Attaching the stack trace for the crash below:
Attaching the code for creating simple cache object:
I also added non-fatal logging on our side to understand the scale at which users are hitting this crash.
From production logs:
Users who crash have ~150 downloaded videos on average
Average video size ~180 MB
Total cached data is typically 25–30 GB
While investigating further, I looked into SimpleCache initialization and noticed a potential performance/memory concern in the recursive directory scan that eagerly loads all cache spans into memory:
From what I understand, this:
Recursively scans the entire cache directory at startup
Creates all SimpleCacheSpans eagerly
Populates contentIndex fully before playback
With large offline libraries, this seems like it could cause memory pressure or long blocking work during app startup.
Questions
Is eager loading of all cache spans during SimpleCache initialization mandatory, or is there a supported way to:
Lazily load cache entries, or
Defer span creation until content is actually requested?
Is it required to maintain a full cache index for all downloaded videos upfront?
Would it be valid/supported to create or prepare cache only when a user attempts to play a video, instead of indexing the entire cache at startup?
Are there recommended best practices for handling large offline libraries (100+ videos) with Media3 to avoid startup crashes or OOMs?
e.g., cache size limits, multiple caches, or alternative cache strategies.
Bug Report
adb bugreportto android-media-github@google.com after filing this issue.