I have a recyclerivew which hold a list view pagers, each viewPager has three pictures.and each picture's size is about 200kb.When load image from the network, the OOM occurs.And if you need, i will send you the 2 hprof file for comparison.I thought if there is something wrong with DownSampler when decodeStream.
803 31961100 byte[] 29 dalvik.system.VMRuntime newNonMovableArray
647 31961100 byte[] 32 dalvik.system.VMRuntime newNonMovableArray
316 31961100 byte[] 31 dalvik.system.VMRuntime newNonMovableArray
289 31961100 byte[] 30 dalvik.system.VMRuntime newNonMovableArray
279 31961100 byte[] 32 dalvik.system.VMRuntime newNonMovableArray
144 31961100 byte[] 29 dalvik.system.VMRuntime newNonMovableArray
8 31961100 byte[] 30 dalvik.system.VMRuntime newNonMovableArray
203 3203859 byte[] 6 org.apache.harmony.dalvik.ddmc.DdmVmInternal getRecentAllocations
502 3167547 byte[] 6 org.apache.harmony.dalvik.ddmc.DdmVmInternal getRecentAllocations
177 2764812 byte[] 32 dalvik.system.VMRuntime newNonMovableArray
66 2764812 byte[] 29 dalvik.system.VMRuntime newNonMovableArray
1000 65548 byte[] 29 com.bumptech.glide.util.ByteArrayPool getBytes
999 65548 byte[] 29 com.bumptech.glide.util.ByteArrayPool getBytes
662 65548 byte[] 32 com.bumptech.glide.util.ByteArrayPool getBytes
661 65548 byte[] 32 com.bumptech.glide.util.ByteArrayPool getBytes
380 65548 byte[] 30 com.bumptech.glide.util.ByteArrayPool getBytes
339 65548 byte[] 31 com.bumptech.glide.util.ByteArrayPool getBytes
338 65548 byte[] 30 com.bumptech.glide.util.ByteArrayPool getBytes
329 65548 byte[] 31 com.bumptech.glide.util.ByteArrayPool getBytes
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.java:307)
at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.java:202)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:140)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:50)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:19)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:39)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:20)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSourceData(DecodeJob.java:189)
at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:176)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:127)
at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
And the log:
12-27 14:49:42.071 601-1008/com.ganlan.poster I/art﹕ Alloc sticky concurrent mark sweep GC freed 7(192B) AllocSpace objects, 0(0B) LOS objects, 4% free, 170MB/178MB, paused 728us total 6.889ms
12-27 14:49:42.084 601-1008/com.ganlan.poster I/art﹕ Alloc partial concurrent mark sweep GC freed 99(4KB) AllocSpace objects, 0(0B) LOS objects, 8% free, 170MB/186MB, paused 609us total 11.547ms
12-27 14:49:42.117 601-1008/com.ganlan.poster I/art﹕ Alloc concurrent mark sweep GC freed 9(12KB) AllocSpace objects, 0(0B) LOS objects, 8% free, 170MB/186MB, paused 829us total 31.672ms
12-27 14:49:42.118 601-1008/com.ganlan.poster I/art﹕ Forcing collection of SoftReferences for 30MB allocation
12-27 14:49:42.137 601-1008/com.ganlan.poster I/art﹕ Alloc concurrent mark sweep GC freed 11(344B) AllocSpace objects, 0(0B) LOS objects, 8% free, 170MB/186MB, paused 524us total 18.585ms
12-27 14:49:42.138 601-1008/com.ganlan.poster E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 31961100 byte allocation with 16777120 free bytes and 21MB until OOM"
12-27 14:49:52.896 601-601/com.ganlan.poster D/poster_PosterCursorAda﹕ image :http://ganlan.qiniudn.com/21de4b26453e355c865cb97765d5d880
12-27 14:49:52.897 601-601/com.ganlan.poster D/poster_PosterCursorAda﹕ image : http://ganlan.qiniudn.com/21de4b26453e355c865cb97765d5d880
12-27 14:49:53.146 601-601/com.ganlan.poster D/poster_PosterCursorAda﹕ image :http://ganlan.qiniudn.com/21de4b26453e355c865cb97765d5d880
12-27 14:49:53.147 601-601/com.ganlan.poster D/poster_PosterCursorAda﹕ image : http://ganlan.qiniudn.com/21de4b26453e355c865cb97765d5d880
12-27 14:50:45.005 601-1009/com.ganlan.poster I/art﹕ Alloc sticky concurrent mark sweep GC freed 1733(133KB) AllocSpace objects, 0(0B) LOS objects, 1% free, 184MB/186MB, paused 630us total 38.637ms
12-27 14:50:45.056 601-1009/com.ganlan.poster I/art﹕ Alloc partial concurrent mark sweep GC freed 255(9KB) AllocSpace objects, 5(152MB) LOS objects, 33% free, 31MB/47MB, paused 627us total 47.905ms
31961100 is a 31mb Bitmap allocation. What are the dimensions of these images? Can you add your Glide.load line?
Usually OOMs indicate either a memory leak or an attempt to load an overly large image or images. There have been memory leaks in Glide in the past, but typically the leak is in the application itself. Do the heap dumps show a memory leak?
The image dimensions is 2448x3264, and the image size is about 200kb,the app got an OOM error while loading three or four image at the same time.Then i got some other images for test, the dimension is about 510x700, and the size is about 200kb and it worked.The heap dumps did't show memory leak.I guess maybe the image dimension is too much.So i'll send you the screenshot and hprof file.And the code is as below:
/**
* Load an image from a url into an ImageView using the default placeholder
* drawable if available.
* @param url The web URL of an image.
* @param imageView The target ImageView to load the image into.
* @param requestListener A listener to monitor the request result.
* @param placeholderOverride A drawable to use as a placeholder for this specific image.
* If this parameter is present, {@link #mPlaceHolderResId}
* if ignored for this request.
*/
public void loadImage(String url, ImageView imageView, RequestListener<String, Bitmap> requestListener,
Drawable placeholderOverride, boolean crop) {
BitmapRequestBuilder request = beginImageLoad(url, requestListener, crop)
.animate(R.anim.image_fade_in);
if (placeholderOverride != null) {
request.placeholder(placeholderOverride);
} else if (mPlaceHolderResId != -1) {
request.placeholder(mPlaceHolderResId);
}
request.into(imageView);
}
public BitmapRequestBuilder beginImageLoad(String url,
RequestListener<String, Bitmap> requestListener, boolean crop) {
return requestManager.load(url)
.asBitmap()
.listener(requestListener)
.transform(crop ? mCenterCrop : mFitCenter);
}
/**
* Load an image from a url into the given image view using the default placeholder if
* available.
* @param url The web URL of an image.
* @param imageView The target ImageView to load the image into.
*/
public void loadImage(String url, ImageView imageView) {
loadImage(url, imageView, false /*crop*/);
}
public class PosterPagerAdapter extends PagerAdapter {
final String[] posters;
public PosterPagerAdapter(String[] posters) {
this.posters = posters;
}
@Override
public int getCount() {
return posters.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View parentView = LayoutInflater.from(container.getContext()).inflate(R.layout.poster_image_view, container, false);
ImageView imageView = (ImageView) parentView.findViewById(R.id.poster);
mImageLoader.loadImage(posters[position].trim(), imageView);
container.addView(parentView, 0);
return parentView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View parentView = (View) object;
container.removeView(parentView);
}
}
This is probably caused by #288, we're just not downsampling these images as much as we could.
That said it also depends on the size of the views. You may want to consider manually decreasing the size of the views or using override to set the size Glide uses. Loading 5-6 30mb images isn't going to work well. If it is #288, then pulling forward should help.
Actually I'am confused that why downloading an 200kb size, 2000*3000 dimensions image would allocate 30mb memory.
In Android, as with most frameworks, Bitmaps are uncompressed. As a result, the size they take in memory is based on the number of pixels in the image and the format of the Bitmap.
Glide uses one of two formats, RGB_565 and ARGB_8888. Glide prefers RGB_565, but since it doesn't support transparency, falls back to ARGB_8888 for certain images (and/or via a setting).
RGB_565 is 2 bytes per pixel, so 2000 * 3000 * 2 = 12000000 or 12mb. ARGB_8888 is 4 bytes per pixel, so 2000 * 3000 * 4 = 24000000 or 24mb. Since your images are somewhat larger, with the ARGB_8888 format, 30mb is about what we'd expect.
Since images of 2k by 3k are typically much larger than the view they would be displayed in, downsampling can help reduce the memory usage by only including a subset (1/2, 1/4, 1/8 etc) of the pixels in the Bitmap. If we don't downsample appropriately, or we can't because the view is sufficiently large, you will end up with a very large Bitmap. Decreasing the view size or using override (or pulling forward past the first for #288) will make it easier for Glide to downsample the images and should reduce the memory used. You could also try fitCenter() or centerCrop() to scale the images down to exactly your view size.
Thank you very much for your detailed explanation, it's really of great help. After digging into the source, i understand it better.Now i switched to DecodeFomat RGB_565 and download smaller pics via server, my problem finally solved! Final question, is there a way to clear the memory after the activity is destroyed.Cause i see a method called "clearMemory" in Glide. The scenario is: I have a viewPagerActivity which loads a set of big images(2 or 3 pictures), and it eats a lot of memory when downloading and decoding.Since Glide has disk cache, I assume it's reasonable to clear bitmap pool and memory cache when the activity is destroyed.But Gilde.get(context).clearMemory seems not help, the memory did't reduce after i leave the activity while i was monitoring memory via android studio.
You can use the ComponentCallbacks interface, override those methods in your Application object and call clear or trim memory on Glide then. There is a callback for when the application is backgrounded I believe.
It's possible that you're not seeing any change with those methods currently because requests are cleared in onDestroy in a viewless Fragment, which may be called before or after your Activity's onDestroy.

does newest Glide solove memory leak in Android 2.3?
Most helpful comment
In Android, as with most frameworks, Bitmaps are uncompressed. As a result, the size they take in memory is based on the number of pixels in the image and the format of the Bitmap.
Glide uses one of two formats, RGB_565 and ARGB_8888. Glide prefers RGB_565, but since it doesn't support transparency, falls back to ARGB_8888 for certain images (and/or via a setting).
RGB_565 is 2 bytes per pixel, so 2000 * 3000 * 2 = 12000000 or 12mb. ARGB_8888 is 4 bytes per pixel, so 2000 * 3000 * 4 = 24000000 or 24mb. Since your images are somewhat larger, with the ARGB_8888 format, 30mb is about what we'd expect.
Since images of 2k by 3k are typically much larger than the view they would be displayed in, downsampling can help reduce the memory usage by only including a subset (1/2, 1/4, 1/8 etc) of the pixels in the Bitmap. If we don't downsample appropriately, or we can't because the view is sufficiently large, you will end up with a very large Bitmap. Decreasing the view size or using override (or pulling forward past the first for #288) will make it easier for Glide to downsample the images and should reduce the memory used. You could also try
fitCenter()orcenterCrop()to scale the images down to exactly your view size.