Android Memory leakage debugging

Source: Internet
Author: User

 

I. Overview 1

2. Poor code that is common in Android (Java) and is prone to memory leakage 1

(1) The cursor is not closed during database query. 2

(2) when constructing the Adapter, the cached convertView 3 is not used

(3) Call recycle () to release memory when the Bitmap object is not in use. 4

(4) Release object reference 4

(5) Other 5

Iii. Memory monitoring tool DDMS --> Heap 5

4. Memory analysis Tool MAT (Memory Analyzer Tool) 7

(1) generate the. hprof file 7

(2) Use MAT to import the. hprof file 8

(3) Use MAT's View tool to analyze memory 8

I. Overview

Java programming is often overlooked, but a very important issue is memory usage. Android applications are mainly written in Java, so this problem also occurs in Android development. This article does not discuss Java programming, but sorts out such problems in Android, especially in application development.

Since the author has not been familiar with Android for a long time, please correct me if you have misstated it.

2. Common bad code in Android (Java) that is prone to memory leakage

Android is mainly used in embedded devices. embedded devices generally do not have high configurations due to some well-known restrictions, especially limited memory. If the code we wrote contains too many improper memory usage, it will inevitably make our devices run slowly or even crash. To ensure secure and fast running of Android applications, each Android application uses a proprietary Dalvik Virtual Machine instance to run. It is incubated by the Zygote service process, that is to say, each application runs in its own process. On the one hand, if the program encounters a memory leak during the running process, it will only kill its own process without affecting other processes (if it is a system process such as system_process, will cause the system to restart ). On the other hand, Android allocates different memory usage ceilings for different types of processes. If the memory used by the application process exceeds the upper limit, the system will consider the memory leakage and kill it. The maximum memory allocated by Android to application processes is as follows:

Location:/ANDROID_SOURCE/system/core/rootdir/init. rc Script

# Define the oom_adj values for the classes of processes that can be

# Killed by the kernel. These are used in ActivityManagerService.

Setprop ro. FOREGROUND_APP_ADJ 0

Setprop ro. VISIBLE_APP_ADJ 1

Setprop ro. SECONDARY_SERVER_ADJ 2

Setprop ro. BACKUP_APP_ADJ 2

Setprop ro. HOME_APP_ADJ 4

Setprop ro. HIDDEN_APP_MIN_ADJ 7

Setprop ro. CONTENT_PROVIDER_ADJ 14

Setprop ro. EMPTY_APP_ADJ 15

# Define the memory thresholds at which the above process classes will

# Be killed. These numbers are in pages (4 k ).

Setprop ro. FOREGROUND_APP_MEM 1536

Setprop ro. VISIBLE_APP_MEM 2048

Setprop ro. SECONDARY_SERVER_MEM 4096

Setprop ro. BACKUP_APP_MEM 4096

Setprop ro. HOME_APP_MEM 4096

Setprop ro. HIDDEN_APP_MEM 5120

Setprop ro. CONTENT_PROVIDER_MEM 5632

Setprop ro. EMPTY_APP_MEM 6144

# Write value must be consistent with the above properties.

# Note that the driver only supports 6 slots, so we have HOME_APP at

# Same memory level as services.

Write/sys/module/lowmemorykiller/parameters/adj

Write/proc/sys/vm/overcommit_memory 1

Write/proc/sys/vm/min_free_order_shift 4

Write/sys/module/lowmemorykiller/parameters/minfree 1536,2048, 4096,5120, 5632,6144

# Set init its forked children's oom_adj.

Write/proc/1/oom_adj-16

Because our applications can use limited memory, we need to pay special attention to memory usage when writing code. The following are some common cases of improper memory usage.

(1) The cursor is not closed during database query.

Description:

The program usually queries the database, but it often does not close after using the Cursor. If our query result set is small, memory consumption is not easy to find. Memory problems can be reproduced only when a large number of operations are performed at a regular time, this will cause difficulties and risks for future testing and troubleshooting.

Sample Code:

Cursor cursor = getContentResolver (). query (uri ...);

If (cursor. moveToNext ()){

......

}

Corrected 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

}

}

}

(2) When constructing an Adapter, no cached convertView is used

Description:

Taking constructing the BaseAdapter of ListView as an example, the method is improved in BaseAdapter:

Public View getView (int position, View convertView, ViewGroup parent)

To provide ListView with the view object required by each item. Initially, the ListView will instantiate a certain number of view objects from the BaseAdapter based on the current screen layout, and the ListView will cache these view objects. When you scroll up the ListView, the view object originally located in the top list item will be recycled and then used to construct the bottom list item that appears. This construction process is completed by the getView () method, getView () the second View convertView parameter is the view object of the cached list item. (convertView is null if no view object is cached during initialization ).

From this we can see that if we don't use convertView, but re-instantiate a View object in getView () every time, it will be a waste of resources and time, which will also increase the memory usage. You can view the process when ListView recycles the view object of list item:

Android. widget. AbsListView. java --> void addScrapView (View scrap) method.

Sample Code:

Public View getView (int position, View convertView, ViewGroup parent ){

View view = new Xxx (...);

......

Return view;

}

Corrected 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;

}

(3) Call recycle () to release memory when the Bitmap object is not in use

Description:

Sometimes we will manually operate Bitmap objects. If a Bitmap object occupies memory, when it is not in use, we can call Bitmap. the recycle () method recycles the memory occupied by pixels of this object, but this is not necessary, depending on the situation. Let's take a look at the comments in the Code:

/**

* Free up the memory associated with this bitmap's pixels, and mark

* Bitmap as "dead", meaning it will throw an exception if getPixels () or

* SetPixels () is called, and will draw nothing. This operation cannot be

* Reversed, so it shoshould only be called if you are sure there are no

* Further uses for the bitmap. This is an advanced call, and normally need

* Not be called, since the normal GC process will be free up this memory when

* There are no more references to this bitmap.

*/

(4) Release Object Reference

Description:

This situation is difficult to describe. Two examples are provided.

Example:

Assume that the following operations are performed:

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 post the operation for processing the obj instance to the MessageQueue of a thread. In the above Code, even if the thread where mHandler is located uses the object referenced by obj, this object will not be reclaimed because DemoActivity. obj still has reference to this object. So if this object is no longer used in DemoActivity, you can release the object reference at the [Mark] position, and the code can be changed:

......

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 telephone service in the system in the LockScreen interface to obtain some information (such as signal strength), we can define a PhoneStateListener object in LockScreen, register it with the TelephonyManager service. For LockScreen objects, a LockScreen object is created when the screen lock interface needs to be displayed. When the screen lock interface disappears, the LockScreen object is released.

However, if you forget to cancel the previously registered PhoneStateListener object when releasing the LockScreen object, LockScreen cannot be recycled by zookeeper. If the screen lock interface is constantly displayed and disappears, a large number of LockScreen objects cannot be recycled, causing OutOfMemory and causing the system_process process to crash.

In short, when an object A with A short life cycle is referenced by an object B with A long life cycle, at the end of A's life cycle, the references to A should be removed from B.

(5) Others

The typical release of resources in Android applications is in onPause (), onStop (), and onDestroy () during the Activity lifecycle () method. This is a basic situation and is not described in detail here. For details, refer to the introduction to the Activity lifecycle in the official documentation to determine when resources should be released.

Iii. Memory monitoring tool DDMS --> Heap

No matter how careful you are, it is impossible to completely avoid bad code. At this time, some tools are required to help us check whether there is any place in the code that will cause memory leakage. The DDMS in Android tools comes with a very good memory monitoring tool Heap (here I use the ADT plug-in of eclipse, and take the real machine as an example, the situation in the simulator is similar ). To monitor the memory usage of an application process using Heap, follow these steps:

1. After eclipse is started, switch to the DDMS perspective and confirm that the Devices view and Heap view are all open;

2. Connect your phone to your computer via USB. Make sure that the phone is in "USB debugging" mode instead of "Mass Storage ";

3. After the connection is successful, the device serial number and running process information are displayed in the Devices view of DDMS;

4. Click the process you want to monitor, such as the system_process process;

5. Click the "Update Heap" icon in the top row of the selected Devices View Interface;

6. Click "Cause GC" in the Heap view;

7. In the Heap view, the details of the memory usage of the selected process are displayed.

Note:

A) clicking the "Cause GC" button is equivalent to requesting a gc operation from the VM;

B) when the memory usage information is displayed for the first time, you do not need to constantly click "Cause GC". The Heap view interface will be refreshed on a regular basis, the changes in memory usage can be seen during the continuous operation of applications;

C) the parameters of the memory usage information can be known Based on the name and will not be described here.

How can we know whether our program has the possibility of Memory leakage. Here, you need to pay attention to a value: In the Heap view, there is a Type called data object, that is, the data object, that is, a large number of class Type objects in our program. In a row of data object, there is a column named "Total Size", whose value is the Total memory of all Java data Objects in the current process. Generally, the size of this value determines whether memory leakage exists. You can judge this as follows:

A) constantly operate on the current application, and observe the Total Size value of the data object;

B) under normal circumstances, the Total Size value will be stable within a limited range. That is to say, because the code in the program is good, the object will not be garbage collected, therefore, although we continuously generate many objects during operations, these objects are recycled during the continuous GC of virtual machines, memory usage will reach a stable level;

C) if the Code does not release the object reference, the Total Size value of the data object will not be significantly reduced after each GC, as the number of operations increases, the value of Total Size increases,

The process is killed until the upper limit is reached.

D) The system_process process is used as an example. In my testing environment, the Total Size of the data object occupied by the system_process process will normally be 2.2 ~ 2.8, and when the value exceeds 3.55, the process will be killed.

 

In short, using the Heap View tool of DDMS can easily confirm whether our program has the possibility of Memory leakage.

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.