Collect and analyze Android memory overflow in various ways

Source: Internet
Author: User

Where the narrative is inappropriate, please correct me.

Android is primarily used in embedded devices, and embedded devices are usually not highly configurable due to some well-known conditions, especially when memory is relatively limited. If we write code that has too much memory usage, it will inevitably make our device run slowly, or even crash. To enable Android apps to run safely and quickly, each Android application uses a proprietary Dalvik virtual machine instance that is hatched by the zygote service process, which means that each application runs in its own process. On the one hand, if the program in the process of running a memory leak problem, will only make their own process is killed, and will not affect other processes (if it is system_process and other system process problems, it will cause a system restart). On the other hand, Android allocates different memory usage caps for different types of processes, and if the application process uses more memory than this limit, it is considered a memory leak by the system and is killed. In-memory location allocated for app processes in ANDROID system:/android_source/system/core/rootdir/init.rc
Due to the limited memory available to the application, there is a need to pay special attention to memory usage when writing code.

The following summarizes some of the improper operations that could lead to memory overflow:
(a) The query database did not close the cursor:
Describe:
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
}
}
}
(ii) When constructing adapter, no cached Convertview is used
Describe:
To construct the baseadapter of the ListView as an example, the method is improved in Baseadapter:
Public View GetView (int position, view Convertview, ViewGroup parent)
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, GetView () 's second parameter view Convertview is the view object of the cached LIS 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-to-void Addscrapview (View scrap) method.
Example code:
Public View GetView (int position, View Convertview, ViewGroup parent) {
View view = new Xxx (...);
... ...
return view;
}
To fix the sample code:
Public View GetView (int position, View Convertview, ViewGroup parent) {
View view = null;
if (Convertview! = null) {
view = Convertview;
Populate (view, GetItem (position));
...
} else {
view = new Xxx (...);
...
}
return view;
}
(iii) Bitmap object is not called recycle () to free memory when in use
Describe:
Sometimes we will manipulate the bitmap object manually, if a bitmap object compares memory, when it is not in use, you can call the Bitmap.recycle () method to reclaim the memory occupied by the pixel of this object, but this is not necessary, depending on the situation. You can look at the comments in the code:
/**
* Free up the memory associated with this bitmap ' s pixels, and mark the
* Bitmap as "dead", meaning it would throw an exception if getpixels () or
* SetPixels () is called, and would draw nothing. This operation cannot is
* Reversed, so it should is called if you is sure there is no
* Further uses for the bitmap. This is a advanced call, and normally need
* Not be called, since the normal GC process would free up this memory when
* There is no more references to this bitmap.
*/
(iv) Releasing a reference to an object
Describe:
Example A:
Suppose you have the following actions
public class Demoactivity extends Activity {
... ...
Private Handler Mhandler = ...
Private Object obj;
public void operation () {
obj = Initobj ();
...
[Mark]
Mhandler.post (New Runnable () {
public void Run () {
Useobj (obj);
}
});
}
}
We have a member variable, obj, in operation () we want to be able to post the operation that handles the obj instance to the MessageQueue of a thread. In the above code, even if the thread mhandler is using the object referenced by obj, the object will not be garbage collected because Demoactivity.obj still retains a reference to the object. So if you no longer use this object in Demoactivity, you can release the object's reference at [Mark], and the code can be modified to:
... ...
public void operation () {
obj = Initobj ();
...
Final Object o = obj;
obj = null;
Mhandler.post (New Runnable () {
public void Run () {
Useobj (o);
}
}
}
... ...
Example B:
Suppose we want to listen to the phone service in the system in the lock screen (lockscreen) to get some information (such as signal strength, etc.), you can define a Phonestatelistener object in the Lockscreen. Register it with the Telephonymanager service at the same time. For Lockscreen objects, a Lockscreen object is created when the lock screen needs to be displayed, and the Lockscreen object is released when the lock screen disappears.
However, if you forget to cancel the Phonestatelistener object that we previously registered when releasing the Lockscreen object, it will cause lockscreen to be garbage collected. If the lock screen interface is constantly displayed and disappears, it will eventually cause outofmemory due to the fact that a large number of Lockscreen objects have no way to be recycled, causing the system_process process to hang out.
In short, when an object A with a shorter life cycle has its reference by a long-life object B, at the end of A's life cycle, a reference to A is cleared in B.
(v) Other
The most typical use of Android applications is to be aware of the need to release resources in the life cycle of the activity, in the OnPause (), OnStop (), OnDestroy () methods, in the case of the appropriate release of resources. Since this is a very basic situation, it is not detailed here to see the official documentation on the activity life cycle to determine when resources should be released.
Three, memory monitoring tool DDMS--Heap
No matter how careful it is, it is impossible to avoid bad code altogether, and tools are needed to help us check if there is a memory leak in the code. The DDMS in Android tools comes with a very good memory monitoring tool heap (here I use the Eclipse ADT Plugin, and in the case of a real machine, like in the emulator). The steps to use the heap to monitor the application process using memory are as follows:
1. After you start eclipse, switch to the DDMS perspective and confirm that the devices view, the heap view, are open;
2. Link the phone to the computer via USB, the link needs to confirm that the phone is in "USB debugging" mode, and not as "Mass Storage";
3. After the link is successful, the serial number of the mobile device and the part of the process that is running on the device will be displayed in the Devices view of DDMS.
4. Click to select the process you want to monitor, such as the system_process process;
5. Click to select the "Update Heap" icon in the top row of icons in the Devices view interface;
6. Click the "Cause GC" button in the heap view;
7. In the heap view, you will see the details of the memory usage of the currently selected process.
Description
A) Clicking on the "Cause GC" button is equivalent to requesting a GC operation from the virtual machine;
b) When the memory usage information is displayed for the first time, there is no need to continuously click on "Cause GC", the heap view interface will be refreshed periodically, and the memory usage changes can be seen during the continuous operation of the application.
c) The parameters of the memory usage information can be understood by name, and will not be mentioned here.
How do we know if our program has the possibility of a memory leak? Here's a value to note: In the middle of the heap view, there is a type called data object, which is an object of the class type that exists in our program. In the data object row, there is a column "total size", whose value is the amount of memory for all Java data Objects in the current process, in general, the size of this value determines whether there is a memory leak. It can be judged as follows:
A) continuously operate the current application, while observing the total size value of the data object;
b) The total size value will be stable in a limited range under normal circumstances, that is, because the code in the program is good, no object is not garbage collection, so that although our continuous operation will continue to generate a lot of objects, and in the process of virtual machine continuous GC, These objects are recycled, and the memory footprint will fall to a steady level;
c) Conversely, if there is a case in the code that does not release the object reference, the total size value of the data object will not come down significantly after each GC, and as the number of operations is increased, the value of total size will become larger.
Until an upper limit is reached, the process is killed.
D) The system_process process is here, for example, the total size of the data object that system_process the memory occupied by the process in my test environment will normally stabilize between 2.2~2.8, And when the value exceeds 3.55, the process is killed.

In summary, using the Ddms heap View tool makes it easy to verify that our program has the possibility of a memory leak.

How does the memory overflow of Android occur? Android virtual machine is a register-based Dalvik, its maximum heap size is generally 16M, and some machines are 24M. So the amount of memory space we can use is limited. OutOfMemory errors occur if our memory footprint exceeds a certain level.

Why is there a situation where memory is not enough? I think there are two main reasons:

    • Due to our program errors, long-term maintenance of certain resources (such as the context) of the reference, resulting in memory leaks, resources caused by the failure to release.
    • Multiple objects that consume too much memory (such as bitmap) are saved, causing memory to exceed the limit.

Third, there is no correct use of static:

Static is a keyword in Java that, when used to decorate a member variable, belongs to that class, not an instance of that class. So with the static modified variable, its life cycle is very long, if it is used to reference some resources to consume too many instances (the context of the most cases), then it should be treated with caution.

    1. public class ClassName {
    2. private static Context Mcontext;
    3. Omitted
    4. }

The code above is dangerous, if the activity is assigned to Mcontext. So even if the activity is OnDestroy, the activity will not be released because there is still a reference to the object that holds it.

Let's give an example of an Android document.

  1. private static drawable Sbackground;
  2. @Override
  3. protected void OnCreate (Bundle state) {
  4. Super.oncreate (state);
  5. TextView label = new TextView (this);
  6. Label.settext ("Leaks is bad");
  7. if (Sbackground = = null) {
  8. Sbackground = getdrawable (R.drawable.large_bitmap);
  9. }
  10. Label.setbackgrounddrawable (Sbackground);
  11. Setcontentview (label);
  12. }

Sbackground, is a static variable, but we find that we do not explicitly save the Contex reference, but when drawable is connected to the view, drawable sets the view to a callback, Because the view is a reference to the context, in fact we still have a reference to the context. This reference chain is as follows:

Drawable->textview->context

So, eventually the context was not released, and a memory leak occurred.

How can we effectively avoid the occurrence of such a reference?

First, you should avoid static member variables referencing resources that consume too many instances, such as context.

Second, the context as far as possible to use application context, because the application context of the life cycle is relatively long, reference it will not be a memory leak problem.

Third, use WeakReference instead of strong references. For example, you can use weakreference<context> mcontextref;

The details of this section can also refer to the article section of the Android documentation.

Four, the wrong use of the thread:

Threads are also an important source of memory leaks. The main reason for a thread's memory leak is that the thread life cycle is not controllable.

  1. public class MyActivity extends Activity {
  2. @Override
  3. public void OnCreate (Bundle savedinstancestate) {
  4. Super.oncreate (savedinstancestate);
  5. Setcontentview (R.layout.main);
  6. New MyThread (). Start ();
  7. }
  8. Private class MyThread extends thread{
  9. @Override
  10. public void Run () {
  11. Super.run ();
  12. Do somthing
  13. }
  14. }
  15. }

This code is very common and simple, it is the form that we often use. But assuming that the mythread run function is a time-consuming operation, when we turn on the thread, it turns the device's horizontal screen into a vertical screen, and normally the activity is recreated when the screen is converted, as we thought, the old activity should be destroyed. However, this is not the case in fact.

Since our thread is the inner class of activity, a reference to activity is saved in Mythread, and when Mythread's run function does not end, Mythread is not destroyed, so the old activity it references is not destroyed. Therefore, there is a memory leak problem.

Some people like to use Android-provided asynctask, but in fact the asynctask problem is even more serious, and thread only has this memory leak problem when the run function does not end. However, the implementation mechanism within the asynctask is the use of threadpoolexcutor, the life cycle of the thread object produced by this class is indeterminate and is beyond the control of the application, so if Asynctask is the inner class of activity, The problem of memory leaks is more likely.

What is the general solution to the memory leak caused by this thread?

First, the internal class of the thread is changed to a static inner class.

Second, use weak references inside the thread to save the context reference.

The model is resolved as follows:

    1. Public abstract class Weakasynctask<params, Progress, Result, weaktarget> extends
    2. Asynctask<params, Progress, result> {
    3. protected weakreference<weaktarget> Mtarget;
    4. Public Weakasynctask (Weaktarget target) {
    5. Mtarget = new weakreference<weaktarget> (target);
    6. }
    7. /** {@inheritDoc} */
    8. @Override
    9. Protected final void OnPreExecute () {
    10. Final Weaktarget target = Mtarget.get ();
    11. if (target! = null) {
    12. This.onpreexecute (target);
    13. }
    14. }
    15. /** {@inheritDoc} */
    16. @Override
    17. Protected final Result doinbackground (params ... params) {
    18. Final Weaktarget target = Mtarget.get ();
    19. if (target! = null) {
    20. Return This.doinbackground (target, params);
    21. } else {
    22. return null;
    23. }
    24. }
    25. /** {@inheritDoc} */
    26. @Override
    27. Protected final void OnPostExecute (result result) {
    28. Final Weaktarget target = Mtarget.get ();
    29. if (target! = null) {
    30. This.onpostexecute (target, result);
    31. }
    32. }
    33. protected void OnPreExecute (Weaktarget target) {
    34. No Default Action
    35. }
    36. Protected abstract Result Doinbackground (weaktarget target, params ... params);
    37. protected void OnPostExecute (weaktarget target, result result) {
    38. No Default Action
    39. }
    40. }

Collect and analyze Android memory overflow in various ways

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.