Memory leaks in Android and how to effectively avoid oom summary

Source: Internet
Author: User

I. The concept of oom and memory leaks

We often encounter oom errors in the Android development process because we do not consider the Dalvik virtual machine memory consumption in the app.

1. What is Oom

OOM: Outofmemoery, as the name implies, is memory overflow. A memory overflow is a memory request that the app requests to the system for exceeding the maximum threshold, and the system no longer allocates extra space, which can cause oom error. In our Android platform, most of the situation is in the picture when the improper processing load.

The Android system allocates a limited amount of memory per application, and when a memory leak occurs in an application, it inevitably causes the application to exceed the memory limit allocated by the system, which results in a memory overflow that causes the application to crash. What is the maximum memory size that Android app can apply for, some say 16MB, some say 24MB. In fact, these answers are right, because Android is open-source operating system, different mobile phone manufacturers actually have to modify this part of the ability to access, so it caused the different brands and different systems of mobile phones, for the app's memory support is not the same, However, we can use the runtime class to get the memory size of the current device's Android system for each application. The app does not create an instance of runtime for us, and Java gives us a single example of how to get Runtime.getruntime (). The MaxMemory () method gets the maximum memory that the system can allocate for the app, and TotalMemory () Gets the amount of memory heap space currently allocated by the app.

2. What is a memory leak

Java uses the forward graph mechanism to automatically check objects in memory through GC (when the check is determined by the virtual machine), and if the GC discovers that one or a group of objects is unreachable, the object is reclaimed from memory. That is, an object is not pointed to by any reference, and the object is recycled when it is discovered by the GC, and if a set of objects contains only references to each other, and no references from outside them (for example, two objects A and B hold references to each other, but no external objects hold references to a or b), This is still not reachable and will be recycled by GC.

In the development of Android, when an object is no longer needed, it should be recycled, and another object that is being used holds its reference so that it cannot be recycled, which causes the object to be reclaimed to remain in the heap memory, and the memory leak is generated.

Memory leak Hazard: Only one, that is, the virtual machine consumes too much memory, causing oom (memory overflow), the program error. For Android apps, it's your user who opens an activity, closes it after use, leaks it, turns it off and leaks it, and then, after a few times, the program consumes more memory than the system limits, FC.

Understanding the causes and effects of memory leaks, what we need to do is to master the common memory leaks, and in the future of Android program development, try to avoid it.

Ii. common memory leaks and solutions 1. Memory leaks caused by single cases

Android's singleton mode is very popular with developers, but using inappropriate words can also cause memory leaks.

Because the static characteristics of the singleton make the life cycle of the singleton as long as the life cycle of the application, it shows that if an object is no longer needed, and the singleton object also holds a reference to the object, the object will not be recycled normally, which results in a memory leak.

The following example:

public class AppManager {        private static AppManager instance;        Private context context;        Private AppManager (Context context) {            This.context = context;        }        public static AppManager getinstance (context context) {            if (instance! = null) {                instance = new AppManager (context) ;            }            return instance;        }    }  

This is an ordinary singleton mode, when creating this singleton, because of the need to pass in a context, so

The length of the life cycle of this context is critical:

1), the incoming is the application context: This will have no problem, because the life cycle of the singleton and the application are the same length;

2), the context of the activity is passed in: when the context corresponding activity exits, because the context and activity of the life cycle as long (activity indirectly inherits from the context), So the current activity exits when its memory is not recycled because the Singleton object holds a reference to the activity.

So the correct singleton should be modified to the following way:

public class AppManager {        private static AppManager instance;        Private context context;        Private AppManager (Context context) {            this.context = Context.getapplicationcontext ();        }        public static AppManager getinstance (context context) {            if (instance! = null) {                instance = new AppManager (context) ;            }            return instance;        }    

This way, no matter what context is passed in, the application context will eventually be used, and the life cycle of the singleton is as long as the application, thus preventing a memory leak.

2. A non-static internal class creates a memory leak caused by a static instance

In Java, a non-static anonymous inner class holds an implicit reference to its outer class, and if you have not considered this, storing the reference causes the activity to be retained rather than being reclaimed by the garbage collection mechanism. The Activity object holds a reference to its view layer and all associated resource files, in other words, if your memory leak occurs in the activity, you will lose a lot of memory space.

There are times when we may be able to start frequent activity, in order to avoid duplicating the same data resources, this will appear:

public class Mainactivity extends appcompatactivity {        private static testresource mresource = null;        @Override        protected void onCreate (Bundle savedinstancestate) {            super.oncreate (savedinstancestate);            Setcontentview (r.layout.activity_main);            if (Mmanager = = null) {                Mmanager = new Testresource ();            }            //...        }        Class Testresource {            //...        }    }

This creates a single instance of a non-static inner class within the activity, which uses the singleton data every time the activity is started, which avoids duplication of resources, but is a memory leak because non-static inner classes hold references to external classes by default. The non-static inner class is used to create a static instance with the same life cycle as the application, which causes the static instance to hold a reference to the activity, causing the memory resource of the activity not to be recycled properly. The right approach is:

Set the inner class as a static inner class or extract the inner class as a single case, use ApplicationContext if you need to use the context.

3. Memory leak caused by handler

Handler the use of memory leaks caused by the most common, usually in the processing of network tasks or encapsulation of some request callback API should be handled by handler, for handler code to write a non-standard is likely to cause a memory leak, the following example:

Handler Mhandler = new Handler () {        @Override public        void Handlemessage (Message msg) {            Mimageview.setimagebitmap (Mbitmap);        }    

The above is a simple use of handler. When using an inner class (including an anonymous class) to create a handler, the handler object implicitly holds a reference to an external class object (usually an activity) (otherwise how can you manipulate the view in activity by handler?). )。 Handler usually comes along with a time-consuming background thread (for example, pulling pictures from the web), which notifies handler via a message mechanism after the task has finished executing (the sample is downloaded), and then handler updates the image to the interface. However, if the user shuts down the activity during a network request, and normally the activity is no longer used, it may be reclaimed during GC checking, but since the thread has not finished executing, the thread holds the handler reference (otherwise how does it send a message to handler?). This handler also holds the activity reference, causing the activity to not be recycled (i.e. memory leaks) until the end of the network request (the sample download is complete). Also, if you performed the handler postdelayed () method:

To do, here again call this Runnable object to implement a timer operation every two seconds

Handler.postdelayed (this, 2000);

This method will load your Handler into a message and push the message into MessageQueue, then there will be a message, MessageQueue------Handler before you set the delay to arrive. Activity's chain, which causes your activity to be held in a reference and cannot be recycled.

This way of creating handler causes a memory leak, because Mhandler is an instance of the handler non-static anonymous inner class, so it holds a reference to the external class activity, and we know that Message Queuing is constantly polling for processing messages in Looper. So when this activity exits, there is an unhandled message in the message queue, or a message is being processed, and the message in messages queue holds a reference to the Mhandler instance, and Mhandler holds a reference to the activity. Therefore, the memory resources of the activity cannot be reclaimed in a timely manner, causing a memory leak.

Workarounds for memory leaks using handler

Method One: Through the program logic to protect.

1). Stop your background thread while the activity is closed. When the thread is stopped, it is equivalent to cutting off the handler and externally connected lines, and the activity will naturally be recycled at the right time.

2). If your handler is a message with a delay, then use the corresponding handler Removecallbacks () method to remove the messaging object from the message queue.

Method Two: Declare the handler as a static class.

Static classes do not hold objects from external classes, so your activity can be recycled at will. The code is as follows:

Static class MyHandler extends Handler {        @Override public        void Handlemessage (Message msg) {            Mimageview.setimagebitmap (Mbitmap);        }    

But it's not so simple. Using the above code, you will find that because handler no longer holds references to external class objects, the program does not allow you to manipulate objects in the activity in handler. So you need to add a weak reference to activity in handler (WeakReference):

Static class MyHandler extends Handler {        weakreference<activity > mactivityreference;        MyHandler (activity activity) {            mactivityreference= new weakreference<activity> (activity);        }        @Override public        void Handlemessage (Message msg) {            final activity activity = Mactivityreference.get ();            if (activity! = null) {                mimageview.setimagebitmap (mbitmap);}        }    

After changing the code to the form above, it is done.

Extension: What is WeakReference?

WeakReference a weak reference, as opposed to a strong reference (which we often refer to), is characterized by the fact that the GC ignores weak references when it recycles, that is, even if a weak reference points to an object, as long as the object is not pointed to by a strong reference (and most often requires no soft reference. But the concept of soft references here can be ignored, and the object is reclaimed when it is checked by the GC. For the above code, after the user has closed the activity, even though the background thread is not finished, the GC will still recycle the activity while checking because only one weak reference from handler points to activity. In this way, the problem of memory leaks will not occur.

4. Memory leak caused by thread

The memory leaks that are caused by threads are usually common, as the following two examples may be written by everyone:

—————— test1            New asynctask<void, Void, void> () {                @Override                protected void doinbackground (void ... params) {                    systemclock.sleep (10000);                    return null;                }            }. Execute ();    —————— test2            New Thread (new Runnable () {                @Override public                void Run () {                    systemclock.sleep (10000);                }            }). Start ();  

Both the async task and the runnable are an anonymous inner class, so they have an implicit reference to the current activity. If the task is not completed before the activity is destroyed, the memory resource of the activity cannot be reclaimed, causing a memory leak. The correct approach is to use static internal classes in the following way:

Static class Myasynctask extends Asynctask<void, Void, void> {private weakreference<context> Weakr                  eference;            Public Myasynctask (Context context) {WeakReference = new weakreference<> (context); } @Override protected void Doinbackground (void ... params) {systemclock.sleep (1                0000);            return null; } @Override protected void OnPostExecute (void aVoid) {Super.onpostexecute (avoi                D);                Mainactivity activity = (mainactivity) weakreference.get ();            if (activity! = NULL) {//...}                }} Static Class Myrunnable implements runnable{@Override public void run () {            Systemclock.sleep (10000);        }}//—————— new Thread (New Myrunnable ()). Start (); New Myasynctask (This). Execute ();  

With the code above, the new thread never holds an implicit reference to an external activity, and the activity is recycled after the configuration changes. This avoids the activity of memory resource leakage, of course, when the activity is destroyed should also cancel the corresponding task Asynctask::cancel (), to avoid tasks in the background waste resources.

If our thread is doing a wireless loop update UI operation, the following code:

private static class MyThread extends Thread {            @Override public            void Run () {while              (true) {                Systemclock.s Leep (+);}}}            

This avoids memory leaks caused by activity not being destroyed, but this thread has a memory leak. In the Java thread is the source of the garbage collection mechanism, that is, the DVM virtual machine in the running system always causes the hardware to hold a reference to all the running states of the process, resulting in the running state being never recycled. Therefore, you must implement the destruction logic for your background thread! Here's a workaround:

private static class MyThread extends Thread {            private Boolean mrunning = false;                @Override public            Void Run () {              mrunning = true;              while (mrunning) {                systemclock.sleep ()}            }                public void Close () {              mrunning = false;            }          }   

When we exit the activity, we can display the call Mthread.close () in the OnDestroy () method to end the thread, which avoids the thread's memory leak problem.

5. Memory leaks due to resource object not shutting down

Resource objects such as (cursor,file files, etc.) often use some buffering, we should close them in time when we are not in use, so that their buffers can recover memory in time. Their buffering exists not only in the Java Virtual machine, but also outside the Java Virtual machine. If we simply set its reference to null without shutting them down, it often causes a memory leak. Because some resource objects, such as Sqlitecursor (in the destructor Finalize (), if we do not close it, it itself will close () closed), if we do not close it, the system will be recycled it also closed it, but this is too low efficiency. Therefore, when the resource object is not in use, it should call its close () function, close it, and then set it to null. Be sure that our resource objects are closed when our program exits.

The process of querying the database is often done, but there are often cases where the cursor is not closed after it has been used. If our query result set is relatively small, the memory consumption is not easy to find, only in the case of a large number of operations in a constant time to reproduce the memory problem, which will give future testing and troubleshooting difficulties and risks.

Example code:

cursor cursor = getcontentresolver (). Query (Uri ...);      if (Cursor.movetonext ()) {        ...        

To fix the sample code:

cursor cursor = NULL;      try {        cursor = getcontentresolver (). Query (Uri ...);        if (cursor! = NULL &&cursor.movetonext ()) {            ...          }      } finally {        if (cursor! = null) {            try {                  cursor.close ();            } catch (Exception e) {                //ignore this             }         }      }  
6, bitmap memory overflow caused by no recovery

The improper handling of bitmap is likely to cause oom, most of which is due to this cause. Bitamp Bitmap is a well-deserved fat kid in Android, so be careful when it comes to operation. Since DALIVK does not take the initiative to recycle, developers need to recycle out when bitmap is not being used. In the process of use, timely release is very important. At the same time, if the requirements allow, you can also go to bitmap for a certain scale, through the Bitmapfactory.options Insamplesize property control. If you just want to get bitmap properties, you don't need to allocate memory based on bitmap pixels, just use Bitmapfactory.options's Injustdecodebounds property when parsing the BMP. Finally, we recommend that you load the network pictures, using soft references or weak references and local cache, recommended to use Android-universal-imageloader or xutils, cattle produced, will be a boutique.

7. When constructing adapter, no cached Convertview is used

To construct the baseadapter of a ListView, for example, a method is provided in Baseadapter:

To provide the ListView with the View object that each item needs. Initially, the ListView instantiates a certain number of view objects from the Baseadapter based on the current screen layout, and the ListView caches the View objects. When you scroll up the ListView, the View object that was originally on the top list item is recycled and then used to construct the newly-appearing list item. This construction process is done by the GetView () method, and the second parameter view convertview of GetView () is the view object of the cached list item (the cache does not have a view object when it is initialized, and Convertview is null.) )。 It can be seen that if we do not use Convertview, but each time in the GetView () to re-instantiate a view object, that is, wasting resources is also a waste of time, it will also make memory consumption more and more. The process of retrieving the View object of the list item can be viewed by the ListView:

Android.widget.AbsListView.java--Voidaddscrapview (View scrap) method.

Example code:

public view GetView (int position, Viewconvertview, ViewGroup parent) {        View view = new Xxx (...);        ... ...        return view;      }           Fix example code: Public    View GetView (int position, Viewconvertview, ViewGroup parent) {        view view = null;        if (Convertview! = null) {        view = Convertview;        Populate (view, GetItem (position));        ...        } else {        view = new Xxx (...);        ...        }        return view;      }
Iii. some suggestions for preventing oom

During the Android development process, it can be difficult to reconcile time-consuming tasks during the activity lifecycle, and you can inadvertently cause a memory leak problem. Here are some tips to help you prevent a memory leak:

1, the rational use of static:

Each non-static inner class instance holds a reference to an external class, and if the reference is an activity reference, the activity will not be reclaimed when it is destroyed. If your static inner class needs a reference to a related activity to ensure that it works, then you need to make sure that you are using a weak reference to the activity in your object, or your activity will have an unexpected memory leak. Note, however, that when this class is used in many places around the world, it is doing so because the life cycle of the static declaration variable is actually the same as the life cycle of the app, somewhat similar to application. If a large number of uses, it will occupy the memory space is not released, a few will also cause the memory of the constant overhead, until hung out. The rational use of static is generally used to modify the basic data types or lightweight objects, to avoid repairing the collection or large objects, commonly used to decorate global configuration items, tool class methods, internal classes.

2. Use Softreference/weakreference/lrucache

There is no such mechanism in Java, Android, when the memory is tight or GC sweep, it can be in time to release some memory, so as to allocate to the place to be allocated. The answer is yes, and Java provides us with two of solutions. If you are concerned about the memory overhead of the app, you can consider using WeakReference, when GC reclamation swept through this area of memory will be recycled, if not so concerned, you can use SoftReference, it will be automatically released in case of insufficient memory request, It can also solve the oom problem. Android since 3.0 also launched the LRUCache class, using the LRU algorithm to free memory, the same can solve oom, if compatible with 3.0 version, please import v4 package. On the question of irrelevant references to the second article, we can consider using WeakReference to wrap the message.

3, cautious handler

Handler + thread is a good choice when it comes to handling asynchronous operations. But believe that when using handler, everyone will encounter a warning situation, this is lint for the developer's reminder. Handler runs on the UI thread, constantly processes messages from MessageQueue, and if handler has a message to process but the activity page has ended, the activity's reference is not actually recycled, which creates a memory leak. Solution, one is to handler.removecallbacksandmessages (null) in the OnDestroy method of the activity, canceling all message processing, including pending messages The second is to declare that the inner class of handler is static.

4, do not always think of the Java garbage collection mechanism will help you solve all memory recovery problems

Like the example above, we thought that the garbage collection mechanism would help us to reclaim the memory we didn't need to use, for example: we need to end an activity, and then its instance and the associated thread are recycled. But the reality is not going to be like our script. Java threads will survive until they are explicitly closed, or their processes are killed by the Android system. So, implementing the destroy logic for your background thread is a detail that you have to keep in mind when you use threads, and in addition, you design the destruction logic based on the activity's life cycle to avoid bugs.

Consider whether you really need to use threads. The framework layer of Android apps gives us a lot of classes that make it easy for developers to perform background operations. For example, we can use loader instead of using threads to perform short asynchronous background query operations during the activity's life cycle, and consider using service to notify the broadcastreceiver of the structure to the UI. Finally, keep in mind that this blog post discussion of threads also applies to asynctask (because Asynctask uses Executorservice to perform its tasks). However, although executorservice can only be used in a short operation (the document says up to a few seconds), the activity memory leaks caused by these methods should never occur.

5. The item cache for ListView and GridView

For mobile devices, especially the hardware of the uneven Android ecosystem, page rendering is actually very time-consuming, Findviewbyid is quite slow. So do not reuse the view, in the time of the list is particularly significant, often the phenomenon of sliding card, so we should be good at reusing the created control. Here are two main points to note:

1) Convertview Reuse

Each item in the ListView requires adapter to call a GetView () method, which passes in a Convertview parameter, and the view returned by this method is the view that this item displays. Android provides a widget called recycler (recurring loop), that is, when the ListView item is scrolled out of the screen view, the corresponding item view is cached in the recycler, and the corresponding item is generated from the The Convertview parameter in the GetView that is called at this point is the view that scrolls out of the screen's cached item, so if you can reuse this convertview, it will greatly improve performance.

2) Reuse with Viewholder

We all know the operation in the GetView () method is this: first create the View object from the XML (inflate operation, we use the Reuse Convertview method optimization), and then in this view to Findviewbyid, Find the control object for each child view of item, such as: ImageView, TextView, and so on. Here the Findviewbyid operation is a tree lookup process, is also a time-consuming operation, so it also needs to be optimized, is to use Viewholder, each item's Child view control object is placed in holder, when the first time the Convertview object is created , the Child view Control object of these item Findviewbyid is instantiated and saved to the Viewholder object. Then use the Convertview Settag to set the Viewholder object to the tag, when loading the ListView item can be removed from the tag directly from the Reuse Viewholder object, You do not need to Findviewbyid to find the child control object for item. This greatly improves performance.

But ANDROID5.L provides us with Recyclerview,recyclerview is the classic listview of Evolution and sublimation, which is more flexible than the ListView, but it also introduces some complexity. The latest V7 Support package adds a new Recyclerview. Recyclerview provides a plug-and-play experience, a high degree of decoupling, exceptional flexibility, and the itemanimator achieves an astonishing effect by setting the different layoutmanager,itemdecoration it offers. And Recyclerview has handled the item cache internally for us, so it's more efficient, more secure, and interested readers can look at it.

Memory leaks in Android and how to effectively avoid oom summary

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.