Memory leak in a practical example

I am constantly struggling with detecting memory leaks. I think I have several memory leaks in my project circular review of progress .

One of my assumptions is that I have a memory leak in the FadeRunnable inner class. But honestly, I don’t know exactly how to find out if this is the source of the problem. Well, when I make a normal script and switch the orientation, I see an increase in memory usage, as shown below. And if I comment on the use of the FadeRunnable class, the steps will be smaller (but still there, so I think this is not the only leak)

memory steps

As soon as I analyze a bunch of heaps, I see something. But actually I don’t know what the meanings mean. What I do is

  • Orientation changes many times
  • Open a bunch of heaps and sort it by "Saved Size"
  • Now when I click on "CircularProgressView", I see 8 rows in the correct area. I guess this means that 8 instances of "CircularProgressView" have leaked and live somewhere like an orphan in memory.

It is right? If so, how can I find out in the dump information (I think, somewhere in the bottom panel) where this object is stored / held.

dump pile

I would like a step-by-step explanation of how to find out if and what kind of object is leaking some kind of memory.

All suspicious representation code can be found in this class.

https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java

But also do not hesitate to familiarize yourself with the complete project for a deeper understanding, and if you want to play with it.

Thanks in advance!

UPDATE

The code link above shows the fixed code for the mem-leaking inner class. The following snippet shows the source code for mem-leaking, which should never be used that way

 /** * Mem-leaking code, for fixed code see repository link * https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java */ public class CircularProgressView extends View { ... private Thread fadeThread = null; ... ... class FadeRunnable implements Runnable { @Override public void run() { ... } } ... ... private void startFade(boolean fadeIn) { // check existing if(this.fadeThread != null) { // check if fade is already running switch(this.fadeThread.getState()) { case TERMINATED: case NEW: this.fadeThread = null; break; case RUNNABLE: case BLOCKED: case TIMED_WAITING: case WAITING: return; } } // create new this.fadeThread = new Thread(new FadeRunnable(fadeIn, this.fadeTime)); this.fadeThread.start(); } } 
+7
java performance android memory memory-leaks
source share
2 answers

Yes, you have a memory leak in the FadeRunnable class.

Each instance of the inner class contains an implicit reference to its outer class, accessible through the OuterClass.this . In your project, when you execute FadeRunnable and then start reconfiguration by changing the orientation, all the activity and your CircularProgressView contained inside are recreated, but the FadeRunnable from the previous one is still alive (selected) and, due to this, keeping an implicit link to your the outer class is CircularProgressView , the view continues to live as well, so after several reconfigurations you have 8 instances of the CircularProgressView allocated in memory, and this gets worse - each view maintains a link to the context and this also cannot be freed, which Bad memory leaks.

Runnables, Handlers, and similar objects that may exit their closing actions, fragments, views, etc., must be declared as standard classes or STATIC inner classes (a static inner class does not contain an implicit reference to its outer class) and should not contain links such as Context , View , etc., you can instead save WeakReference<> , so when your Activity recreated through a configuration change, the View can be destroyed and freed by the garbage collector.

This is a very informative article on this subject, I highly recommend reading it.

+5
source share

I think you have the right direction there. This FadeRunnable is certainly not cool. Even if you have other memory leaks, you should check this out.

In general, what you really need to do in the view is completely different, especially that the views already have the ability to handle time and animation without the need for threads.

I offer you, as I consider it, a simpler and more understandable approach to animation on presentations.

  • Start by removing the fully executable file and stream.

to start the animation:

 ValueAnimator animation = ValueAnimator.ofFloat(0, 1); animation.setDuration(500); animation.addUpdateListener(animationUpdate); animation.addListener(animationUpdate); animation.start(); 

and then you need these listeners

  // this gets called for every animation update, // inside this call you update `CircularProgressView.this.fadeAlpha` private final ValueAnimator.AnimatorUpdateListener animationUpdate = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // this fraction varies between 0f and 1f float fraction = animation.getAnimatedFraction(); // ... do your calculation ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } }; // this is an optional one only if you really need // in that you get notified when the animation starts and ends private final Animator.AnimatorListener animationListener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // anything u need goes here ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } @Override public void onAnimationEnd(Animator animation) { // anything u need goes here ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } }; 

and what about it.

On the topic of actually analyzing a memory leak, I suggest you from now on use the canary leak library forever: https://github.com/square/leakcanary is a great tool to help us (developers) track memory leaks.

change

Why do you have a memory leak in this animation? It is pretty simple:

  • on startFade(boolean); you create a new thread and a new runnable
  • runnable has a view reference (because it is a non-static inner class)
  • There is a link to Runnable in the stream, so you can run it.
  • the structure destroys the view because it is no longer part of the user interface (rotation, return button)
  • the thread still works with the loop still running, while the View object is still not destroyed, because Runnable refers to it.
  • The view object has an instance of Context , and this context is Activity .

So, at the end of this sequence, your activity will not be garbage collected by GC, AKA: Memory Leak!

+2
source share

All Articles