Memory leaks: Garbage-Collection Root - java

In this case I am trying to understand some concepts related to memory leaks in Android. Though it is a frequent question, I am more interested on a real justification rather than a simple answer like "do not keep a reference to the activity".
These are the pages I've been reading so far:
link1: Garbage-Collection Roots—The Source of All Object Trees
link2: Memory leaks in Android
link3: Memory leaks in Android
and my conclusion so far is that the only way to have memory leaks with activities is when a component has a reference to an Activity and the lifecycle of this component is beyond the activity's lifecycle. The time you have a memory leak depends on the lifecycle of that component.
With that being said, I found this discussion with Jake Wharton related to Dagger 2 in an MVP architecture, where he wrote:
There is no leak. If the activity has outgoing references to
dependencies and there are no incoming references to the activity it
will be garbage collected.
As far as I know, if I have an activity which has a reference to a presenter which has a reference to a view interface (the implementation is the same activity), and then the activity is being recreated with a new presenter, then I do not see the reason why we can have memory leaks: the old activity and presenter are not longer reachable and thus they are potentially garbage collected. The only way I can have memory leak is if the presenter or any component inside the presenter has a lifecycle beyond the activity's lifecycle.
My questions are:
Am I missing something here?
Does having a reference to an activity mean I will have memory leaks? If that so, then does it mean the activity is considered a Garbage-Collection Root

It took me time to get back to this question and get all the information possible to understand how this works and see if the statement "if you have an object referring to an Activity which has been replaced by another activity, then you have a memory leak" is true.
Firstly, I decided to run some code to see the memory's behaviour. I used a simple MVP project I have.
The first thing I have done is to keep a reference to the activity in my presenter after the onDestroy method has been called:
public abstract class BasePresenter<T> {
//Activity or Fragment attached to Presenter
protected T view;
public void onViewCreated(T view) {
this.view = view;
}
public void onStart() {
}
public void onResume() {
}
public void onPause() {
}
public void onStop(){
}
public void onDestroyed() {
// view = null;
}
}
Then I turned on the "do not keep activities in memory" option and recreated the activity a couple of times.
When I dumped the memory I saw multiple instances of that activity, but after calling the garbage collector with the android memory profiler option they disappeared (as you know the GC is not called every time due to performance issues, just when it is needed).
So in this case the statement "if you have an object referring to an Activity which has been replaced by another activity, then you have a memory leak" is not true, or at least it is not complete to be true. In order to get a memory leak, that dependency graph needs to be referenced by a GC root.
Then I decided to uncomment that line in onDestroy:
public void onDestroyed() {
view = null;
}
and have a static reference to a view in my fragment (views have a reference to the activity where they are created)
private static View viewLeak;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
progressBar = view.findViewById(R.id.progress);
if(viewLeak == null){
viewLeak = progressBar;
}
}
Again, I repeated the process of recreating the activity and in this case I had multiples instances of the same activity (two instances: one which was actively used and another one referenced by the static view). Of course I called the garbage collector but that extra instance of the activity stayed in memory. What changed here is that in this case we have a GC root which is the static variable:
Static variables are referenced by their classes. This fact makes them de facto GC roots. Source
Of course, there are multiple cases where you can get a CG root and thus, multiple cases where you can get memory leaks.
I hope this helps you to clarify some concepts related to memory leaks in Java as it did to me.
Other sources that were useful to understand this are:
LINK1, LINK2, LINK3
NEW CONTENT:
This is a video from google I/O talking about memory leaks. Please, pay attention when he says "long-live objects having a reference to an Activity" - minute 25:30.

Suppose you have an object A that has a reference to Activity B. Now suppose you finish the Activity B. You can be sure that B is ready to be garbage-collected.
Now suppose you manually call the GC. Will it recycle the Activity B? No! Why? Because there's one object referring to it (object A).
This is how a memory leak is born.

Related

Java / weak references : weak reference.get() is not null while there is no any more strong reference holding it

Here is my very simple code :
class UpdateAlertActivity extends Activity {
O o ;
WeakReference<O> _o ;
ArrayList<O> arr = new ArrayList<>();
static class O {
private String l ;
public O(String l) {
this.l = l ;
}
}
void test()
{
arr.clear();
Runtime.getRuntime().gc();
Log.i(LOG_TAG, "breakpoint");
}
#Override
protected void onResume()
{
super.onResume();
o = new O("hello");
arr.add(o);
_o = new WeakReference<>(o);
test();
}
}
As I clear array in test() method, I was excepting _o.get() to be null at my breakpoint, but it's not the case. When I see in the debbuger where o is held, it shows me only in the weak reference _o, while I thought weak reference are made to free their instance when there is no other strong reference on them...
I saw on StackOverflow that I missed calling GC, but calling it had no effect in my program as you can see.
EDIT :
Even this super simple code does not work as excpected, it makes no sense...
O o = new O("lol");
WeakReference<O> _o = new WeakReference<>(o);
Log.i(LOG_TAG, "1:" + _o.get().toString());
o = null ;
Log.i(LOG_TAG, "2:" + _o.get().toString());
System.gc();
Log.i(LOG_TAG, "3:" + _o.get().toString()); // output not null here
EDIT2 :
I wanted the use of GC because I have, in another static class, an array called lastUpdates, which is filled with app updates sent by my server wia TCP, sometimes.
All my activities observes this lastUpdate array (by implementing Observer/Observable interface), and when it is changed, all are notified, and call a particular function onUpdate(ArrayList u) with the list of new arrived updates as a parameter. This function process the updates only if the activity is resumed (changing UI during activity sleep causes app to crash).
If the entiere app is "sleeping" (user is in device menu for example), I want only the first activity waking up to be able to process new updates.
For that, I created in my activity class a array pendingPersistentUpdates storing weak references of all updates that has been catch when activity was sleeping. When the first activity wakes up, the updates catch during app sleep time are still in lastUpdates array, so I expect the weak references stored in pendingPersistentUpdates activity prop to return the actual updates, so my activity can UI-process them, at onResume.
I was expecting this situation :
An activity A run, an activity B is paused (behind activity A for example)
App receives an update U. All activities (A and B) are notified. A process the update because it's running, as expected. B store this update in its pendingPersistentUpdates, because it's paused.
A pause, user returns to B. At A pause, lastUpdates array is cleared, so the weak references of the updates in B pendingPersistentUpdates would return null
B resume, but pendingPersistentUpdates weak references are null (lastUpdates has been cleared), so U is not processed.
Though, as lastUpdate.clear() does not fire GC, B pendingPersistentUpdates updates still exist, and are processed a second time (behavior not desired)
It is not possible to force the garbage collector to run. Runtime.getRuntime().gc() is just an alias for System.gc(), and both are just a hint (read the docs; they mention this). In particular, calling gc() usually means the collection will occur in some other thread; so it's more the 'start sign' for the collector; gc() doesn't necessarily pause and wait around for the gc to finish a full cycle, it merely tells the gc to start one. Whilst, again, no guarantee, Thread.sleep(10000L); makes it more likely you'll see the effects of the GC call.
A WeakReference merely guarantees that the existence of this object won't impede on any garbage collection of the object it refers to. That's all. It doesn't instantaneously lose its referent when the only way to get to the referent is via WeakReference objects. That'll happen later (in fact, that will happen before the object is GCed: First the collector will wipe out all weakrefs, and sometime later will the memory that the object occupied be truly free for use). So, what you're observing in your debugger (that the only way to get to the object is via that WR) isn't inherently broken.
Note also that the debugger itself can be the issue. Merely being there and observing it can have an effect (so make sure you run it without as well).
If you want the GC to try harder, you can allocate some fairly large arrays and pause the thread, that can help, but there is nothing you can do to guarantee a GC run. If you want to observe, run java with java -verbose:gc restOfArgsHere, and keep an eye on the console; if a GC actually occurs, you'll see it.
However.
Your code as pasted doesn't even compile which suggests you either pasted only half of it, or you edited it some 'for clarity' (often a bad idea, best to paste precisely what you're working with!). In particular, you write o = new O("hello");, but o isn't defined anywhere. If it is a field of your class, it would imply the referent cannot be collected at all! So you might want to check on that. Make that O o = new O("hello"); instead of what you have. Assuming you read your debugger correctly and it's functioning properly, this isn't it, but it is suspicious.
Calling the garbage collector only suggests to the system that it should free up memory, it does not force it explicitly. This means that the collection may not actually happen if there is enough memory.
https://developer.android.com/reference/java/lang/System#gc()
Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
Notice the next sentence "the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects"
Internally the garbage collector uses heuristics / thresholds to decide when to collect unused objects, so, basically when the JVM needs memory.

Getting a "this field leaks a context object" when passing a context from Main Activity to Java Class via Constructor

I have the following 2 lines of code in my MainActivity class passing a value:
RecyclerView mRecyclerView = findViewById(R.id.act_recyclerview);
new LoadText(this,mRecyclerView).execute();
In my LoadText class, I am consuming as follows:
private Context mContext;
private RecyclerView mRecyclerView;
public LoadText(Context context, RecyclerView recyclerView){
this.mContext=context;
this.mRecyclerView=recyclerView;
}
My code works fine, however Android Studio is warning of a resource leak when I hover over mContext and mRecyclerView. I have looked for solutions to similar problems like mine and it was suggested to pass the value via a constructor, which is what I am doing so I am not sure I understand why.
This is a warning, which means that the system knows that something might be wrong, but can't say 100% for sure.
In this case, the question is how long your LoadText object lives for. If it lives longer than the Activity or the RecyclerView that you pass to it, your code will cause a memory leak: the LoadText has a strong reference to the Activity, which will prevent it from being garbage collected.
If you, as a human, know that your LoadText object will never live longer than the Activity (or will only do so for a brief amount of time), then you can safely ignore this warning. If, on the other hand, the LoadText object lives for a long time, it could potentially keep your activity alive forever.
Practically speaking, leaking a single Activity isn't the end of the world. But if you do things like this a lot, eventually your app will run out of memory and crash.
It's also possible that the zombie Activity will wind up executing code that you don't expect (maybe the LoadText class calls a method on it), which could lead to undesirable behavior or a crash.

Making static reference variable nullify will ever be garbage collected by java?

I have a question that looks very easy and simple but I am bit confused. My question is, in Android application I am making static reference of activity for some purposes. And in onDestroy() I am making that activity reference null while assigning null value explicitly. Is it good? Will it be eligible for garbage collection? will it also cause memory leak? Please help me in this regard.
This is the code I am using for activity reference in onCreate() method
mInstance = this;
And this is how in onDestroy() I am assigning null value
#Override
protected void onDestroy() {
super.onDestroy();
mInstance = null;
}
I'm not an Android developer, so I'll answer your question without taking into account that.
Yes, the space allocated by your Activity in the heap will be collected (may be collected) once no more references to it exists. Like every other object in Java.
Being static or not doesn't change that.
Although honestly I don't understand why
mInstance = this;
Could you explain in more details?
This isn't thread safe at all, so be careful if you're working on a concurrent environment.
Yes if you nullify it will be garbage collected, but assigning an Activity to a static variable is an anti-pattern and must be avoided at all costs, even if you eventually nullify it, because this is easiest way to leak an Activity.
In fact, Android Studio should already be giving you a warning about it.
Here you have some literature to avoid leaking memory.
People will hate me for this but in my own modular architecture i like to use them on android. Yes it is a memory leak if you do not use them properly but it saves time and time means $$$ in my book. When i need to parse a Java Object from an activity to another activity there is diferent options. putExtra(JsonString), putExtra(Parseable), putExtra(Serializable). But i just like to create a new static file to hold all the objects i need temporary with a static public function to nullify all of them after getting them in the other activity. Be carefull if you do this.

Memory Leak in practical example

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
Change orientation many times
Open heap dump and sort by 'Retained Size'
Now when I click on "CircularProgressView' I see 8 rows in the right area. I guess this means there are 8 instances of the 'CircularProgressView' leaked and living somewhere as orphan in the memory.
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!
UPDATE
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();
}
}
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.
I guess you have the correct direction there. This FadeRunnable is certainly not cool. Even if you have other memory leaks, you defo should check this out.
In general what you should really be doing in a view is quite different, specially that views already have facilities to deal with timing and animation without the need for threads.
I'll suggest you what I believe is a simpler and cleaner approach to animate stuff on views.
Start by removing your runnable and thread completely.
then to start an animation you do:
ValueAnimator animation = ValueAnimator.ofFloat(0, 1);
animation.setDuration(500);
animation.addUpdateListener(animationUpdate);
animation.addListener(animationUpdate);
animation.start();
and then you need those 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 that's about it.
On the topic of actual memory leak analysis I'll suggest you to from now on and forever use the leak canary library: https://github.com/square/leakcanary it's a great tool to help us (developers) track memory leaks.
edit:
Why are you having a memory leak on this animation?
It's quite simple:
on startFade(boolean); you create a new thread and a new runnable
the runnable have a reference to the view (because it's an non-static inner class)
the thread have a reference to the Runnable, so can run it.
the framework destroy the view, because it's not part of the UI anymore (rotation, back button)
the thread is still running, with the runnable still looping, with the View object still not destroyed because the Runnable references it.
the view Object have an instance of Context, and this context is the Activity.
So at the end of this sequence your activity will not be garbage collected by the GC, AKA: Memory Leak !

Memory leak - Android activities held by android.os.Message

I'm working on an application where 90% of the activities inherit from a common activity and all these activities leak, meaning that if I go from A->B and then B->A (finish() is called), B's onDestroy() gets called but it still leaks (checked with MAT).
The leaked activities are quite big (10MB~) so after going back and forth a few times the app crashes with OOM.
I've checked the heap dump and followed the path to GC roots for the leaking activities and they all look like this:
So I guess it's something in the common superclass that's leaking. I've already checked that all the BroadcastReceivers and listeners are unregistered when the activities get destroyed and there are no Handlers used nor anonymous inner classes which may be causing the leak (it seems to me, at least).
What could be the cause of the leak? Any help would be really appreciated.
EDIT
I've found that there are two pieces of code that when commented out the activities are not leaking anymore:
Some lines of code that instantiate a ProgressDialog.
A call to postDelayed with an anonymous Runnable.
In the first case, the dialog's dismiss() function is called before destruction, so I don't know why this could be the problem. In the second case, removeCallbacks is called for the Runnable in onPause, so theoretically it's properly cleaned, isnt' it?
Cheers.
The problem turn out to be the anonymous Runnable in postDelayed. This Runnable was called with the postDelayed() function of the content view of the base activity, so it was something like this:
#Override
protected void onResume() {
...
mCallback = new Runnable() { ... };
getContentView().postDelayed(mCallback, mDelay);
}
The surprising part was that this callback was removed in onPause():
#Override
protected void onPause() {
...
getContentView().removeCallbacks(mCallback);
mCallback = null;
}
Why this didn't prevent the leak is still a mistery to me. At first I tried using getContentView().getHandler().removeCallbacksAndMessages(null) and it fixed the leak, but then the app had things completely broken apart in seemingly unrelated places.
In the end what fixed the leak was creating a Handler instance and calling postDelayed() and removeCallbacks() on this handler.

Categories

Resources