I am constantly struggling with identifying memory leaks. I guess I have several memory leaks in my project circular progress view.
One of my guesses is I have a memory leak in the internal class FadeRunnable
.
But to be honest I don't know exactly how to find out if this is exactly the source of the problem. Well, when I do the usual scenario and switch the orientation I see an increase of the memory usage as shown below. And if I comment out the usage of the FadeRunnable
class the steps are smaller (but still there, so I guess that's not the only leak)
Once I analyze the heap dump, I see something. But actually I don't know what the values mean. The things I do is
Is this correct? If so, how can I find out in the dump information (I guess somewhere in the lower pane) where this object is saved/held.
I would love to have a step-by-step explanation how to find out if and which object is leaking some memory.
All of the code of the suspected view can be found in this class.
https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java
But also feel free to check out the full project for deeper insight and if you want to play around with it.
Thanks in advance!
The code link from above shows the fixed code of the mem-leaking inner class. The following snippet shows the original mem-leaking code that should never be used like that
/**
* 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();
}
}
The memory leak occurs, when a piece of memory which was previously allocated by the programmer. Then it is not deallocated properly by programmer. That memory is no longer in use by the program. So that place is reserved for no reason. That's why this is called the memory leak.
Memory leaks are when programs on the computer incorrectly manage memory allocations. This is not uncommon on modern software and can cause performance drags on the system. The easiest way to fix this issue is to close and reopen the program with the leak, as it will reset the allocations.
What is a Memory Leak in Java? The standard definition of a memory leak is a scenario that occurs when objects are no longer being used by the application, but the Garbage Collector is unable to remove them from working memory – because they're still being referenced.
Yes, you do have a memory leak in FadeRunnable
class.
Every instance of inner class contains implicit reference to its outer class, accessible through OuterClass.this
operator. In your project, when you execute the FadeRunnable
and then trigger reconfiguration by orientation change, the whole activity and your CircularProgressView
contained within get recreated, but the FadeRunnable
from previous is still alive (allocated) and, because of it holding implicit reference to its outer CircularProgressView
class, the view continues to live also, that's why after several reconfigurations you have 8 instances of CircularProgressView
allocated in memory, and that gets worse - every View keeps a reference to it's context, and this cannot be freed also, resulting in bad memory leaks.
Runnables, Handlers and similar objects that can out-live their enclosing activities, fragments, views etc. should be declared as standard classes or STATIC inner classes (a static inner class doesn't hold implicit reference to its outer class), and shouldn't keep references such as Context
, View
etc., instead you can keep a WeakReference<>
so when your Activity
is recreated through config change, the View
can be destroyed and freed by garbage collector.
This is a very informative article on the subject, I strongly suggest reading it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With