bumptech / glide

An image loading and caching library for Android focused on smooth scrolling
https://bumptech.github.io/glide/
Other
34.49k stars 6.11k forks source link

OutOfMemoryError for very large GIFs, even when skipping the memory cache #4677

Closed perracodex closed 2 years ago

perracodex commented 2 years ago

Glide Version: 4.12.0

Integration libraries: None

Device/Android Version: Redmi Note 10 Pro / Android 11

Issue details / Repro steps / Use case background:

This is actually an edge case, but shows that there may be a problem with the GIF decoder or related resource decoder. The issue happens when trying to load a unusually large GIF. For my test I used a 194Mb GIF file with a 360x640 resolution. The GIF fails to load showing an OutOfMemoryError issue in the logcat.

The GIF load configuration is set to skip Memory & Disk caches. Even tested overriding the max size to something very low as 1 pixel size to see what happens, but still throws the error.

Glide load line / GlideModule (if any) / list Adapter code (if any):

GlideApp.with(context)
              .asGif()
              .load(uri)
              .priority(priority)
              .diskCacheStrategy(DiskCacheStrategy.NONE)
              .skipMemoryCache(true)
              .error(fallbackDrawable)
              .placeholder(placeholder)
              .format(DecodeFormat.PREFER_RGB_565)
              .override(maxWidth, maxHeight)
              .fitCenter()
              .into(target);

Stack trace / LogCat:

2021-10-30 17:20:53.072 25415-25597/redacted W/redacted: Throwing OutOfMemoryError "Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912" (VmSize 6463036 kB)
2021-10-30 17:20:53.102 25415-25415/redacted W/Glide: Load failed for content://media/external/images/media/12879 with size [360x640]
    class com.bumptech.glide.load.engine.GlideException: Failed to load resource
    There were 2 root causes:
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368858496, growth limit 536870912)
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912)
     call GlideException#logRootCauses(String) for more detail
      Cause (1 of 2): class com.bumptech.glide.load.engine.GlideException: Failed LoadPath{AutoCloseInputStream->GifDrawable->GifDrawable}, LOCAL
    There was 1 root cause:
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368858496, growth limit 536870912)
     call GlideException#logRootCauses(String) for more detail
        Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Failed DecodePath{AutoCloseInputStream->GifDrawable->GifDrawable}
    There was 1 root cause:
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368858496, growth limit 536870912)
     call GlideException#logRootCauses(String) for more detail
          Cause (1 of 1): class java.lang.OutOfMemoryError: Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368858496, growth limit 536870912
      Cause (2 of 2): class com.bumptech.glide.load.engine.GlideException: Failed LoadPath{AutoCloseInputStream->GifDrawable->GifDrawable}, LOCAL
    There was 1 root cause:
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912)
     call GlideException#logRootCauses(String) for more detail
        Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Failed DecodePath{AutoCloseInputStream->GifDrawable->GifDrawable}
    There was 1 root cause:
    java.lang.OutOfMemoryError(Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912)
     call GlideException#logRootCauses(String) for more detail
          Cause (1 of 1): class java.lang.OutOfMemoryError: Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912
2021-10-30 17:20:53.104 25415-25415/redacted I/Glide: Root cause (1 of 2)
    java.lang.OutOfMemoryError: Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368858496, growth limit 536870912
        at java.util.Arrays.copyOf(Arrays.java:3161)
        at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
        at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
        at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.inputStreamToBytes(StreamGifDecoder.java:64)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.decode(StreamGifDecoder.java:49)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.decode(StreamGifDecoder.java:23)
        at com.bumptech.glide.load.engine.DecodePath.decodeResourceWithList(DecodePath.java:92)
        at com.bumptech.glide.load.engine.DecodePath.decodeResource(DecodePath.java:70)
        at com.bumptech.glide.load.engine.DecodePath.decode(DecodePath.java:59)
        at com.bumptech.glide.load.engine.LoadPath.loadWithExceptionList(LoadPath.java:76)
        at com.bumptech.glide.load.engine.LoadPath.load(LoadPath.java:57)
        at com.bumptech.glide.load.engine.DecodeJob.runLoadPath(DecodeJob.java:529)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromFetcher(DecodeJob.java:493)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromData(DecodeJob.java:479)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromRetrievedData(DecodeJob.java:430)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherReady(DecodeJob.java:394)
        at com.bumptech.glide.load.engine.SourceGenerator.onDataReadyInternal(SourceGenerator.java:148)
        at com.bumptech.glide.load.engine.SourceGenerator$1.onDataReady(SourceGenerator.java:76)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onDataReady(MultiModelLoader.java:145)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:45)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
        at com.bumptech.glide.load.model.stream.QMediaStoreUriLoader$QMediaStoreUriFetcher.loadData(QMediaStoreUriLoader.java:141)
        at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:280)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
2021-10-30 17:20:53.106 25415-25415/redacted I/Glide: Root cause (2 of 2)
    java.lang.OutOfMemoryError: Failed to allocate a 268435472 byte allocation with 25165824 free bytes and 184MB until OOM, target footprint 368828136, growth limit 536870912
        at java.util.Arrays.copyOf(Arrays.java:3161)
        at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
        at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
        at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.inputStreamToBytes(StreamGifDecoder.java:64)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.decode(StreamGifDecoder.java:49)
        at com.bumptech.glide.load.resource.gif.StreamGifDecoder.decode(StreamGifDecoder.java:23)
        at com.bumptech.glide.load.engine.DecodePath.decodeResourceWithList(DecodePath.java:92)
        at com.bumptech.glide.load.engine.DecodePath.decodeResource(DecodePath.java:70)
        at com.bumptech.glide.load.engine.DecodePath.decode(DecodePath.java:59)
        at com.bumptech.glide.load.engine.LoadPath.loadWithExceptionList(LoadPath.java:76)
        at com.bumptech.glide.load.engine.LoadPath.load(LoadPath.java:57)
        at com.bumptech.glide.load.engine.DecodeJob.runLoadPath(DecodeJob.java:529)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromFetcher(DecodeJob.java:493)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromData(DecodeJob.java:479)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromRetrievedData(DecodeJob.java:430)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherReady(DecodeJob.java:394)
        at com.bumptech.glide.load.engine.SourceGenerator.onDataReadyInternal(SourceGenerator.java:148)
        at com.bumptech.glide.load.engine.SourceGenerator$1.onDataReady(SourceGenerator.java:76)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:45)
        at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromRetrievedData(DecodeJob.java:438)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherReady(DecodeJob.java:394)
        at com.bumptech.glide.load.engine.SourceGenerator.onDataReadyInternal(SourceGenerator.java:148)
        at com.bumptech.glide.load.engine.SourceGenerator$1.onDataReady(SourceGenerator.java:76)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onDataReady(MultiModelLoader.java:145)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:45)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
        at com.bumptech.glide.load.model.stream.QMediaStoreUriLoader$QMediaStoreUriFetcher.loadData(QMediaStoreUriLoader.java:141)
        at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:280)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
perracodex commented 2 years ago

I'm closing the issue, as I've found out that glide's GifDecoder always loads the full GIF data into memory by design. It decodes each from dynamically one at a time, yet the actual GIF data is always loaded into memory. I was with the wrong impression that was loading 1 frame at time from disk, but this is not the case. Probably the GifDecoder needs some refactoring for efficiency, maybe using the NDK.