Why need to take care memory leak in android?

Sharing is caring!

In this tutorial, We will learn why we need to take care memory leak to every android developer? I think every android developer has faced OutOfMemoryError on multiple times while building any android app. This is not only error but application crashed as well and every developer and user are not like a crash or any kind of issue.

So we need to investigate why application gives OutOfMemoryError? What is the reason behind that? What is the root cause?

Generally, I was getting OutOfMemoryError while creating a bitmap from a file or downloading the image and loading on image view. So I was thinking that image size might be very large that why it’s allocating a lot of heap memory. And while searched on stack overflow android community suggested that resize your bitmap. I applied the same solution then somehow error handled and is gone for that moment.  But after playing with the application for a while, I was still getting the same error. Then I felt that this is not the correct solution which I am looking.

I started to figure out what is the actual reason behind this error? The community helped me and suggested that you need to forcibly call the root garbage collector or GC to reclaim your memory. Then I thought that, why garbage collector is not removing the objects? It might be that GC is not working smartly in Java. No, I was wrong  Garbage Collector is the best pattern which is built in Java.

I think every developer was doing the same thing to fixed this error. But this is not the correct investigation to solve this error. Actually, OutOfMemoryError could be any place and anywhere in our code and very hard to fix to developers.

Actually, the root cause of OutOfMemoryError is a memory leak. Let’s check what is a memory leak? and why need to take care to every developer before start writing the single line of code? Everyone knows this famous mantra “One leak will sink a ship“. That’s true.

What is a memory leak?

Some objects have a limited lifetime. When their job is done, they are expected to be garbage collected. If a chain of references holds an object in memory after the end of its expected lifetime, this creates a memory leak. When these leaks accumulate, the app runs out of memory.

For example, after Activity.onDestroy() is called, the activity, its view hierarchy, and their associated objects, variables, bitmaps or thread running should all be garbage collectible. If a thread running in the background holds a reference to the activity, then the corresponding memory cannot be reclaimed. This eventually leads to an OutOfMemoryError crash.

When your app has memory leaks, it continuously increases the heap size of memory.  it cannot claim memory from unused objects. As a result, it will ask Android system for more memory. But there is a limit. The system will eventually refuse to allocate more memory for your app. When this happens, app user will get an out-of-memory crash.

What are the reasons of the memory leak?

There are the many reasons that activity holding the reference of objects and system are not allowing to claim those object by Garbage collector. This actually happens to write the bad code practice by developers.

1. Static Reference: – We need to know before the declare any variable or field as a static. Static variants are alive until the application get killed. GC only claim those objects which do not hold any reference. So how can we take care? Let’s see an example of memory leak and solution to avoid.

Leak Memory:

public class TestMemoryLeakStaticVariable extends AppCompatActivity{

    private static Context context= null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak_context);

        if (context == null) {
            context = this;
        }
    }

}

Best Practice to avoid leak memory:

public class TestMemoryLeakStaticVariable extends AppCompatActivity{

    private static Context context= null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak_context);

        if (context == null) {
            context = this;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        context = null;
   }
}

2.  Any Listener or Receiver

 private LocationRequest createLocationRequest() {
        LocationRequest mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        return mLocationRequest;
    }

To avoid the memory leak we need to remove listener or receiver on Activity.Destroy() or Activity.onStop() as per your requirement.

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
    }

3. Inner Classes Type of Background thread.

public class TestMemoryLeakThread extends AppCompatActivity{

    private TextView mMessageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak_context);
        mMessageView = (TextView) findViewById(R.id.test_leak);

        new LongRunningTask().execute();
    }

    private class LongRunningTask extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {
            return "Return back to main thread to update on UI";
        }

        @Override
        protected void onPostExecute(String result) {
            mMessageView.setText(result);
        }
    }
}

To avoid leak memory we need to declare thread as static week reference and cancel the thread on Activity Lifecycle of Activity.onDestroy(). The reason behind that when Activity gets killed by Android System because of the memory low. In this case Android will follow Activity Lifecycle if the activity gets killed and remove the holding reference or assign with null.

public class TestMemoryLeakThread extends AppCompatActivity{

    private TextView mMessageView;
    private AsyncTask mLongRunningTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak_context);
        mMessageView = (TextView) findViewById(R.id.test_leak);

        mLongRunningTask = new LongRunningTask(mMessageView).execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLongRunningTask.cancel(true);
    }

    private static class LongRunningTask extends AsyncTask<Void, Void, String> {

        private final WeakReference<TextView> messageViewReference;

        public LongRunningTask(TextView messageView) {
            this.messageViewReference = new WeakReference<>(messageView);
        }

        @Override
        protected String doInBackground(Void... params) {
            String message = null;
            if (!isCancelled()) {
                message = "Return back to main thread to update on UI";
            }
            return message;
        }

        @Override
        protected void onPostExecute(String result) {
            TextView view = messageViewReference.get();
            if (view != null) {
                view.setText(result);
            }
        }
    }
}

If you are using Handler rather than Async Tasks Then try to use WeakHandler to avoid a memory leak.
4. Non-static anonymous class: – Non static anonymous class hold an implicit reference to their enclosing class. So make those as a static reference to avoid the leak. Let’s see an example of a thread.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak_to_runnable);

        new MyThread().start();
    }

    // FIXME: non-static anonymous classes hold an implicit reference to their enclosing class.
    // Fix is to make it static. Also, close thread in activity onDestroy() to avoid thread leak. See `LeakThreadsActivity`
    private class MyThread extends Thread {
        @Override
        public void run() {
            while (true) {
                SystemClock.sleep(1000);
            }
        }
    }
}

Solution

private static Thread mThread;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_leak_to_runnable);

    mThread = new MyThread();
    mThread .start();
}

private class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            SystemClock.sleep(1000);
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
     mThread.close();
 }
}

These are the few example that I presented here but these are not enough, there are much more. So we need to take care those are.

How to know Memory Leak?

In java Memory Analyzer is very famous tool to understand the memory leak. It will parse the memory heap dump and showing the result of those objects which are holding the object reference and allocated heap memory. Even Android also have a good tool that we can analyze the memory leak.

Leak Canary – Leak Canary is the best tool to monitor the heap memory. This is open source java library which is given by Square. Internally it runs the service in a different process to watching the heap allocation. It will parse the memory heap dump and showing the result of those objects which are holding the object reference and allocated heap memory. Using this library is pretty easy and simple. You need to add few lines in your code. You can check on official GitHub link.

So whenever memory leak happened or memory is going to low. it will notify with a dialog to the developer and will create the beautiful tree of objects on a different app which are holding the reference and allocating heap.

Android Studio – Android studio provides an option to analyze the memory and showing the beautiful graph on memory allocation.  You can initiate the GC root and get the heap dump and show the tree object view to analyze that which object allocating the heap dump. If you installed the Leak Canary then you can filter the leaking class and identify that how many objects having memory leaks in this particular class. Here you can check how android studio monitors heap memory.

Here you can analyze the heap dump and do filters to check the leaked result of a class.

Wrapping – If we are taking care these small -2 leaks then our apps become to next level and every user will be showing interest into your awesome app. I know it is difficult to handle all memory leaks because of the limited time of project but at least we can handle the leak of a big object like Bitmap. Sounds good 🙂

If you are using Kotlin in place of Java then this post will help you to a quick start to using Kotlin in Android.

Ok so please do subscribe your email to get the newsletter of this blog on below and if you like this post then do not forget to share like and comment on below section.

Happy coding 🙂

Reference to learn more check here and here.

0 0 votes
Article Rating

Similar Posts

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments