-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Resource re use in Glide
For information on Glide v4, see the documentation. This wiki covers Glide v3 only.
Glide re-uses resources to avoid unnecessary allocations. Dalvik (before Lollipop) has two basic modes for garbage collection, GC_CONCURRENT
and GC_FOR_ALLOC
. GC_CONCURRENT
blocks the main thread twice for about 5ms for each collection. Since each operation is less than a single frame (16ms), GC_CONCURRENT
tends not to cause your application to drop frames. In contrast, GC_FOR_ALLOC
is a stop the world collection that can block the main thread for 125+ms. GC_FOR_ALLOC virtually always causes your application to drop multiple frames, resulting in visible stuttering, particularly while scrolling.
Unfortunately Dalvik seems to handle even modest allocations (a 16kb buffer for example) poorly. Repeated moderate allocations, or even a single large allocation (say for a Bitmap), will cause GC_FOR_ALLOC. Therefore, the more you allocate, the more stop the world garbage collections you incur, and the more frames your application drops.
By re-using moderate to large resources, Glide helps keep your app jank free by avoiding a substantial number of stop the world garbage collections.
Glide takes a permissive approach to re-use resource, which means that although Glide will opportunistically re-use resources when it believes it is safe to do so, Glide does not require users to recycle resources after each request.
Glide uses two simple signals to identify resources that can be re-used:
-
Calling
clear()
on a View or a Target indicates both that Glide should cancel any in progress loads and that it is safe for Glide to return any resources (Bitmaps, byte arrays etc) displayed in the Target to a resource pool. Users may manually callclear()
at any time, however they are not typically required to do so due to #2: -
View or Target re-use
Whenever a user starts a new load into an existing View or Target, Glide first calls
clear()
on the View or Target to cancel any previous load and/or re-use any displayed resources. As a result, Glide will automatically pool resources and manage loads for users who re-use Views in ListView or RecyclerView.
To avoid unnecessary work, if two requests are made that map to the same resource, Glide will the single resource to both callers. As a result, a single signal that a resource is no longer used is not sufficient. To avoid recycling resources that are used by one caller, but not the other, Glide uses reference counting to track resources.
Providing a given resource to a Target or a View increments the reference count for the resource by one. Clearing a given Target or View decrements the reference count by one. When the reference count drops to zero, Glide will recycle the resource and return its contents to any available pools.
Glide's Resource API contains a recycle()
method that is called when Glide believes a resource has no more consumers.
Glide currently provides a BitmapPool interface that allows Resources to obtain Bitmap
and re-use Bitmap objects. Glide's BitmapPool can be obtained from any Context using the Glide singleton:
Glide.get(context).getBitmapPool();
ResourceDecoders are free to return any implementation of the Resource API they wish, so users can customize or provide additional pooling for novel types by implementing their own Resources and ResourceDecoders.
Similarly users who want more control over Bitmap pooling are free to implement their own BitmapPool, which they can then provide to Glide using a GlideModule.
Unfortunately pooling makes it difficult to assert that a user isn't misusing a resource or a Bitmap. However, there are two primary indicators that something might be going wrong with Bitmap pooling in Glide.
-
Cannot draw a recycled Bitmap
Glide's BitmapPool has a fixed size. When Bitmaps are evicted from the pool without being re-used, Glide will call
recycle()
. If an application inadvertently continues to hold on to the Bitmap even after indicating to Glide that it is safe to recycle it, the application may then attempt to draw the Bitmap, resulting in a crash. -
Can't call reconfigure() on a recycled bitmap
Resources are returned to Glide's BitmapPool when they're not in use any more. This is handled internally based on the lifecycle of a
Request
(who controlsResource
s). If something callsrecycle()
on those Bitmaps, but they're still in the pool, Glide cannot re-use them and your app crashes with the above message. One key point here is that the crash will likely happen in the future at another point in your app, and not where the offending code was executed! -
Views flicker between images or the same image shows up in multiple views
If a Bitmap is returned to the BitmapPool multiple times, or is returned to the pool but still held on to by a View, another image may be decoded into the Bitmap. If this happens, the contents of the Bitmap are replaced with the new image. Views may still attempt to draw the Bitmap during this process, which will result either in artifacts or in the original View showing a new image.
The most common causes of these issues are:
-
Attempting to load two different resources into the same Target.
There is no safe way to load multiple resources into a single Target in Glide. Users can use the
thumbnail()
API to load a series of resources into a Target, but it is only safe to reference each resource until the next call toonResourceReady()
.Users who want to load multiple resources into the same View can do so by using two separate Targets. To make sure that the loads don't cancel each other, users either need to avoid ViewTarget subclasses, or use a custom ViewTarget subclass and override
setRequest()
andgetRequest()
so that they do not use the View's tag to store the Request. -
Loading a resource into a Target, clearing or reusing the Target, and continuing to reference the resource.
The easiest way to avoid this error is to make sure that all references to a resource are nulled out when
onLoadCleared()
is called. It is generally safe to load a Bitmap and then de-reference the Target. It is not safe to load a Bitmap, clear the Target, and continue to reference the Bitmap. -
Recycling the original
Bitmap
in aTransformation<Bitmap>
.As the JavaDoc says in
Transformation
the original Bitmap will be automatically recycled when needed. This is an important difference from other loader libraries, for example Picasso.BitmapTransformation
provides the boilerplate to handle Glide'sResource
creation, but the recycling is done internally, so bothTransformation
andBitmapTransformation
must not recycle the passed-inBitmap
orResource
.
Worth noting the any intermediate bitmaps that the custom transformation gets from pool, but does not return fromtransform
must be either put back to the pool or be recycled, but never both. See also how to handle re-use in Transformations. Take a look at https://github.com/wasabeef/glide-transformations for some custom transformations.
For information on Glide v4, see the documentation. This wiki covers Glide v3 only.