Glide Version: 3.7
Integration libraries: OkHttp3
Device/Android Version: Redmi 2 API 19
Issue details / Repro steps / Use case background: My app's heap size goes up to about 30mb on the initial load, and after using for quite some time hits about 55-60 MB. This seems really high as there are some devices with memory limits as low as 16 MB. MAT shows that the culprit is likely to be a bunch of Bitmaps (33% usage) however I'm at loss as to what to do next.
I have a swipeable Viewpager (4 tabs). Each tab has a fragment with a recyclerview, which loads a bunch of images. It's very similar to Instagram.
Glide load line / GlideModule (if any) / list Adapter code (if any):
public class HomeFeedAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<HomeFeedPosts.data>postsList;
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
private final RequestManager glide;
public HomeFeedAdapter(List<HomeFeedPosts.data> postsList,RequestManager glide){
this.postsList = postsList;
this.glide = glide;
}
public void refreshRecyclerView(List<HomeFeedPosts.data>postsList){
this.postsList.clear();
this.postsList.addAll(postsList);
notifyDataSetChanged();
}
public void addMoreItem(List<HomeFeedPosts.data>postsList){
this.postsList.addAll(postsList);
notifyDataSetChanged();
}
public void loadProgressBar(){
postsList.add(null);
notifyItemInserted(postsList.size() - 1);
}
public void removeProgressBar(){
postsList.remove(postsList.size()-1);
notifyItemRemoved(postsList.size());
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.home_feed_post_photo, parent, false);
return new PostsViewHolder(itemView);
} else if (viewType == VIEW_TYPE_LOADING) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.progressbar_layout_loadmore, parent, false);
return new ProgressBarViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof PostsViewHolder){
HomeFeedPosts.data postsData = postsList.get(position);
((PostsViewHolder) holder).title.setText(postsData.user.username);
glide.load(postsData.images.standard_resolution.url)
.priority(Priority.IMMEDIATE)
.placeholder(R.drawable.grey_placeholder)
.into(((PostsViewHolder) holder).imagePost);
glide.load(postsData.user.profile_picture)
.priority(Priority.LOW)
.placeholder(R.drawable.grey_placeholder)
.into(((PostsViewHolder) holder).imageAvatar);
} else if (holder instanceof ProgressBarViewHolder){
((ProgressBarViewHolder) holder).progressBar.setIndeterminate(true);
}
}
@Override
public int getItemCount() {
return postsList.size();
}
static class PostsViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private FixedImageView imagePost;
private ImageView imageAvatar;
public PostsViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.username);
imagePost = (FixedImageView) itemView.findViewById(R.id.imagePost);
imageAvatar = (ImageView) itemView.findViewById(R.id.imageAvatar);
}
}
static class ProgressBarViewHolder extends RecyclerView.ViewHolder {
private ProgressBar progressBar;
public ProgressBarViewHolder(View itemView) {
super(itemView);
progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar1);
}
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
@Override
public int getItemViewType(int position) {
if (postsList.get(position) == null) return VIEW_TYPE_LOADING;
return VIEW_TYPE_ITEM;
}
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder);
if (holder instanceof PostsViewHolder){
Glide.clear(((PostsViewHolder) holder).imagePost);
Glide.clear(((PostsViewHolder) holder).imageAvatar);
}
}
}
Layout XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginBottom="12dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:background="#ffffff"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
tools:src="@drawable/img2"
android:id="@+id/imageAvatar"
android:layout_margin="5dp"
android:layout_width="30dp"
android:layout_height="30dp" />
<TextView
android:layout_weight="8"
fontPath="fonts/GothamRounded-Medium.otf"
android:singleLine="true"
android:textColor="@color/AlmostBlack"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:id="@+id/username" />
<Button
fontPath = "fonts/Lato-Bold.ttf"
android:text="FOLLOW"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:background="@drawable/roundbuttons"
android:layout_width="wrap_content"
android:layout_height="25dp" />
</LinearLayout>
<com.saphala.gokilpedia_mobile.util.FixedImageView
tools:src = "@drawable/img2"
android:id="@+id/imagePost"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Stack trace / LogCat:
paste stack trace and/or log here



From the images it looks like you're observing the normal pooling. Try to get a heap dump / memory usage count after calling Glide.get(context).clearMemory(), that should free up space. But it shouldn't be called in production as pooling is why Glide is so fast. If you think about it if an app doesn't use the memory it's given then it's wasting it. It's much easier to grab an already allocated ~1M Bitmap than create a new one. If you try Glide on a lower end phone you should see that the pool is smaller to accommodate for the constraints.
Note that onViewRecycled is only called when you're scrolling. To force freeing resources when the ViewPager page is changed you need to setAdapter(null) on the list so the active Bitmaps are freed.
Where exactly should Glide.get(context).clearMemory() be called?
Also, I noticed that the heap is not reset to zero after quitting the app, is there any way to clear the heap?
I usually add a button on options menu, it doesn't matter where the call is since it's effect is global, the key is when you call it: before taking a dump.
Glide will clear all requests when the component passed into Glide.with is destroyed, so there should be leak.
Again, you might be only seeing the pool. My Galaxy S4's default behaviour is to leak the entry activity, I couldn't find where I found this, it's somewhere in the comments on GitHub. And while the application is alive, Glide.glide.bitmapPool is not garbage collected. The Android system will kill your app if it needs memory though, so it's not a big deal.
Sorry for the late reply, I've been busy lately. After playing with my app for a while I notice that even though I'm not doing anything (the app is idle), memory allocation keeps increasing at a rate of roughly 10kb every few seconds. Is this behavior caused by glide?
AS/IDEA > Android Monitor > Memory > Start Allocation Tracking
The tracker doesn't show anything. It's empty
Try a shorter period: http://b.android.com/204503
Did you managed to get some data out of it?
Glide should only do stuff while idle when there are GIFs playing.
Closing this as there's no response. ViewPagers keep multiple pages in memory, if each is a list then you have a lot of images showing and Glide can't do anything about that. Don't load the images, or clear the images when they're not actually visible.
@TWiStErRob ,I have the similar scene and code like this. I have some problems need to solve.
ViewPager ,it is quick to increase memory to 120M or heigher, and cause OOM.activity which hold the ViewPagerdestroyed,but the memory did not release. from the MAT shows that GlideDrawableImageViewTarget and Bitmap may cause the memory leak.this is MAT message:
@moooy Please open a new issue with more details. From what you've given it's either that you have a list in the ViewPager, or you're using the application context to load, but could be anything else too...
This helped me remove the leaks when using an adapter:
Glide.with(context)
.load(imageURL))
.asBitmap()
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
if (bitmap != null) {
imageView.setImageDrawable(bitmap);
}
}
});
hi in view model for frament
The activity context is kept even when you press the back button and exit the program.
And go back to the program, you do not need to re-value the context because the context is static
`@Override
public void onBindViewHolder(@NonNull final viewholder viewholder, final int i) {
final Model_Post model_post = list.get(i);
Log.e(TAG, "onBindViewHolder: "+model_post.getImageurl());
Glide.with(MainActivity.activity)
.load(Uri.parse(model_post.getImageurl()))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewholder.itemsPostBinding.imgvItempost);
}`
You must do this in main activity :
`public static AppCompatActivity activity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = MainActivity.this;
}`