12/08/2018, 14:37

Android Memory Leaks

Android memory leak is a problem which is found specially while testing the application. It's happened when the heap storage is created in a program but it can't release the discarded memory. Suppose, you made an awesome application, maybe you found your applicaiton is running smooth in a ...

Android memory leak is a problem which is found specially while testing the application. It's happened when the heap storage is created in a program but it can't release the discarded memory.

Suppose, you made an awesome application, maybe you found your applicaiton is running smooth in a powerful device, but suddenly you found some lags in less-powerful or less-storaged device because of the memory leak. This is an "UNWANTED" problem that no developer expect! There are several reasons to cause a memory leak. However, there are 02 types of memory leak:

  1. Out-of-memory (OOM) errors: This error is occured when the program hits the memory limit. Although, it's difficult to identify the cause of leakage without receiving the crash report.
  2. Memory Leakage for holding object: This type of memory leakage occurs if an object's task is finished but still it's running for a long time. Even the garbage collector is also useless in this case if any application hold those objects.

There is a sample error-log is shown below when a memory leak arises:

    08-27 14:11:15.307    1857-1857/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.tazeen.classnkk, PID: 1857
    java.lang.OutOfMemoryError: Failed to allocate a 23970828 byte allocation with 2097152 free bytes and 2MB until OOM
            at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
            at android.graphics.Bitmap.nativeCreate(Native Method)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:812)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:789)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:709)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:634)

Generally, Java has a garbage collector, so memory management is skipped in many Android apps because of the dangerous trust on the concept that one doesn't need to worry about memory, which causes a lot of "lazy" apps! The Garbage collector can only remove the objects which are completely free. The best way to avoid leaks is prevention but when you want to encounter a memory issue then there are few tools will be helpful as following:

  1. Eclipse Memory Analyzer (MAT) : It provides the analysis of how much storage is being used by the applications. It’s quite helpful to track the memory leakage.
  2. Valgrind : These tools can automatically detect android memory leak & threading bugs inside the Android apps.
  3. DDMS : DDMS (Dalvik Debug Monitor Server) can directly sense storage allocation and help developers to work on memory leakage in android applications as it displays memory leakage in apps.
  4. LeakCanary : LeakCanary is an Open Source library to detect memory leaks in your debug builds.

  • Wrong selection of context: Context objects are so common which is mostly used & also misused! Loading resources, launching a new Activity, obtaining a system service, getting internal file paths, and creating views all require a context. For example since a toast can be seen in many activities instead of in just one, use getApplicationContext() for toasts.

See the table below to find the right context:

  • Properly not finishing the services: Sometimes, mistakenly the developers forgot to finish the service after task done. For example an intentService that use google location service api which need to disconnect the service as following:
if (googleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, GoogleLocationService.this);
        googleApiClient.disconnect();
    }
  • Image and bitmaps usage with large volume: Any picture can be huge in memory, each pixel is taking 32 bits in memory. You should load a reduced version of the image instead of the full size. Suppose, you are using Square's library Picasso, then you maybe creates memory-leak by not using the .fit() as following:
Picasso.with(ActivityExample.this)                   //Activity context
                .load(object.getImageUrl())           
                .fit()                                //This avoided the OutOfMemoryError
                .centerCrop()                         //makes image to not stretch
                .into(imageView);
  • Inner classes: A very common data structure in Java is the so called inner classes. They are popular since they can be defined as only the enclosing class can instantiate them. These kind of classes will create an implicit reference to the enclosing class which is prone to errors especially if the two classes have different life-cycles. For example, developers are trent to use AsyncTask to do many tasks in background. A common mistake with AsyncTask is to capture a strong reference to the host Activity (or Fragment):
class MyActivity extends Activity {
  private AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
    // Here, Inner classes implicitly keep a pointer to their parent.
  }
}

This is a problem because AsyncTask can easily outlive the parent Activity, for example if a configuration change happens while the task is running. The right way to do this is to make your task a static class, which does not capture the parent, and holding a weak reference to the host Activity:

class MyActivity extends Activity {
  static class MyTask extends AsyncTask<Void, Void, Void> {
    // Weak references will still allow the Activity to be garbage-collected
    private final WeakReference<MyActivity> weakActivity;

    MyTask(MyActivity myActivity) {
      this.weakActivity = new WeakReference<>(myActivity);
    }

    @Override
    public Void doInBackground(Void... params) {
      // do async stuff here
    }

    @Override
    public void onPostExecute(Void result) {
      // Re-acquire a strong reference to the activity, and verify
      // that it still exists and is active.
      MyActivity activity = weakActivity.get();
      if (activity == null
          || activity.isFinishing()
          || activity.isDestroyed()) {
        // activity is no longer valid, don't do anything!
        return;
      }
      // The activity is still valid, do main-thread stuff here
    }
  }
}
  • Others: If you are using broadcast receivers unregister them. Also, when you are using java.util.Observer (Observer pattern) then make sure to use deleteObserver(observer).

References:

http://www.theshiftingbit.com/Fixing-Memory-Leaks-in-Android-Studio http://blog.nimbledroid.com/2016/05/23/memory-leaks.html https://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1 http://www.twinlogix.com/en/blog/memory-management-android-apps http://www.slideshare.net/tarasleskiv/android-memory-fundamentals

0