How to optimize memory in Android development

How to optimize memory in Android development

Editor's note: Some of the content in this article may be controversial (such as the comments in the picture below). You can absorb useful content from it and read it with an attitude of learning and communication, rather than reading it with an attitude of accusation.

Many people think that JAVA programs should not have memory leaks because of the garbage collection mechanism. In fact, if an object is no longer used in a program, but there are still references to it, the garbage collector cannot recycle it. Of course, the memory occupied by the object cannot be used, which causes a memory leak. If our java runs for a long time and this memory leak continues to occur, there will be no memory available. Of course, the memory leak of java is different from that of C/C++. If the java program is completely finished, all its objects will be unreachable, and the system can perform garbage collection on them. Its memory leak is limited to itself and will not affect the entire system. The memory leak of C/C++ is worse. Its memory leak is at the system level. Even if the C/C++ program exits, its leaked memory cannot be recovered by the system and will never be used unless the machine is restarted.

A memory leak in an Android application has little impact on other applications. In order to enable Android applications to run safely and quickly, each Android application will use a dedicated Dalvik virtual machine instance to run, which is hatched by the Zygote service process, that is, each application runs in its own process. Android assigns different memory usage limits to different types of processes. If a memory leak occurs during the running of the program and the memory used by the application process exceeds this limit, it will be regarded as a memory leak by the system and killed. This means that only the process itself is killed without affecting other processes (if there is a problem with system processes such as system_process, it will cause the system to restart).

1. Memory leak caused by reference not being released

1.1 Memory leak caused by not canceling registration

This kind of Android memory leak is more serious than pure Java memory leak, because some other Android programs may reference objects of our Anroid program (such as registration mechanism). Even if our Android program has ended, other referencing programs still have references to an object of our Android program, and the leaked memory still cannot be garbage collected.

For example, Example 1:

Assume that we want to listen to the phone service in the system to obtain some information (such as signal strength, etc.) in the lock screen interface (LockScreen), then we can define a PhoneStateListener object in LockScreen and register it with the TelephonyManager service. For the LockScreen object, a LockScreen object will be created when the lock screen interface needs to be displayed, and the LockScreen object will be released when the lock screen interface disappears.

However, if we forget to cancel the PhoneStateListener object we registered before when releasing the LockScreen object, the LockScreen object cannot be garbage collected. If the lock screen interface is constantly displayed and disappeared, it will eventually cause OutOfMemory due to the large number of LockScreen objects that cannot be recycled, causing the system_process process to hang.

Although some system programs seem to be able to automatically cancel registration (of course not in time), we should still cancel registration explicitly in our program, and all registrations should be canceled when the program ends.

1.2 Memory leaks caused by not cleaning up objects in the collection

We usually add some object references to the collection, and when we don't need the object, we don't clear its reference from the collection, so the collection will become larger and larger. If the collection is static, the situation is even more serious.

2. Memory leak caused by not closing resource objects

Resource objects such as (Cursor, File, etc.) often use some buffers. When we are not using them, we should close them in time so that their buffers can reclaim memory in time. Their buffers exist not only in the Java virtual machine, but also outside the Java virtual machine. If we just set its reference to null without closing them, it will often cause memory leaks. Because some resource objects, such as SQLiteCursor (in the destructor finalize(), if we do not close it, it will call close() to close it by itself), if we do not close it, the system will also close it when it recycles it, but this efficiency is too low. Therefore, when a resource object is not in use, its close() function should be called to close it, and then set to null. Be sure to make sure that our resource objects are closed when our program exits.

Database queries are often performed in programs, but the Cursor is often not closed after use. If our query result set is relatively small, memory consumption is not easy to detect, and memory problems will only occur when a large number of operations are performed over a long period of time, which will bring difficulties and risks to future testing and troubleshooting.

3. Some bad code causes memory pressure

Some codes do not cause memory leaks, but they either fail to release unused memory effectively and promptly, or fail to effectively utilize existing objects but frequently apply for new memory, which has a great impact on memory recovery and allocation, and easily forces the virtual machine to allocate more memory to the application process, resulting in unnecessary memory overhead.

3.1, Bitmap does not call recycle()

When the Bitmap object is not in use, we should first call recycle() to release the memory, and then set it to null. Although recycle() should release the main memory of Bitmap immediately according to the source code, the test results show that it does not release the memory immediately. However, it should still greatly speed up the release of the main memory of Bitmap.

3.2, When constructing the Adapter, the cached convertView is not used

Taking the BaseAdapter of ListView as an example, the following methods are provided in BaseAdapter:

public View getView(int position, View convertView, ViewGroup parent) is used to provide ListView with the view objects required for each item. Initially, ListView will instantiate a certain number of view objects from BaseAdapter according to the current screen layout, and ListView will cache these view objects. When the ListView is scrolled up, the view object of the original top list item will be recycled and then used to construct the new bottom list item. This construction process is completed by the getView() method. The second parameter of getView(), View convertView, is the view object of the cached list item (if there is no view object in the cache during initialization, convertView is null).

From this we can see that if we do not use convertView, but re-instantiate a View object every time in getView(), it will waste time, cause memory garbage, and increase the pressure of garbage collection. If garbage collection is too late, the virtual machine will have to allocate more memory to the application process, causing unnecessary memory expenses. The process of ListView recycling the view object of the list item can be viewed:

view plaincopy to clipboardprint?

android.widget.AbsListView.java –> void addScrapView(View scrap) method.

In Android development, we must always pay attention to memory allocation and garbage collection, because the system allocates limited memory to each Dalvik virtual machine. In Google's G1, the allocated heap size is only 16M, and later machines are generally 24M, which is really pitiful. Therefore, we need to pay attention to it during the development process. Don't cause OOM errors due to your own code problems.

JAVA's memory management:

As we all know, the Android application layer is developed by Java, and the Android Davlik virtual machine is similar to the JVM, except that it is register-based. Therefore, to understand Android's memory management, you must understand Java's memory allocation and garbage collection mechanism.

In Java, memory is allocated for objects through the new keyword, and memory is released by the garbage collector (GC). Engineers do not need to explicitly manage memory during development. However, this may unknowingly waste a lot of memory, eventually causing the Java virtual machine to spend a lot of time on garbage collection, and even more seriously causing the JVM to OOM. Therefore, it is necessary for Java engineers to understand JAVA's memory allocation and garbage collection mechanisms.

The following lists the existing problems. Click on detail to see the detailed code that may have problems:

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-32625.png

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-21158.png

For more information about using MAT, please refer to: http://www.blogjava.net/rosen/ … .html

This brother wrote it in more detail.

Summarize

Whether it is Java or Android, you should understand the memory allocation and garbage collection mechanism. It is difficult for engineers to write code without bad code. The key is how to troubleshoot Android memory optimization when problems occur.

1. Android's memory mechanism

Android programs are written in Java, so Android's memory management is similar to Java's memory management. Programmers allocate memory for objects through new, and all objects are allocated space in the Java heap; however, the release of objects is completed by the garbage collector. The memory mechanism in C/C++ is "whoever pollutes, whoever cleans it up", while Java is more humane, and we have a special cleaner (GC) for us.

So how can GC confirm whether an object has been abandoned? Java adopts the principle of directed graph. Java considers the reference relationship as a directed edge of the graph, and the directed edge points from the referrer to the referenced object. The thread object can be used as the starting vertex of a directed graph. The graph is a tree starting from the starting vertex. Objects that can be reached from the root vertex are all valid objects, and GC will not recycle these objects. If an object (connected subgraph) is unreachable from this root vertex (note that the graph is a directed graph), then we believe that this (these) object is no longer referenced and can be recycled by GC.

2. Android memory overflow

How does Android memory overflow occur?

Android's virtual machine is based on the register Dalvik, and its maximum heap size is generally 16M, and some machines are 24M. Therefore, the memory space we can use is limited. If our memory usage exceeds a certain level, an OutOfMemory error will occur.

Why is there insufficient memory? I think there are two main reasons:

Due to errors in our program, references to certain resources (such as Context) were kept for a long time, causing memory leaks and resources not being released.

Multiple objects that consume too much memory (such as Bitmap) are saved, causing the memory limit to exceed.

3. Evil static

Static is a keyword in Java. When it is used to modify a member variable, the variable belongs to the class rather than the instance of the class. Therefore, the life cycle of a variable modified with static is very long. If it is used to reference some instances that consume too many resources (most often Context), it should be treated with caution.

sBackground is a static variable, but we found that we did not explicitly save the reference to Context. However, when Drawable is connected to View, Drawable sets View as a callback. Since View contains the reference to Context, we actually still save the reference to Context. The reference chain is as follows:

Drawable->TextView->Context

Therefore, the Context was not released in the end and a memory leak occurred.

How can we effectively avoid this kind of citation?

You should try to avoid using static member variables to reference instances that consume too many resources, such as Context.

Try to use Application Context as Context, because the life cycle of Application Context is longer and there will be no memory leak problem when referencing it.

Use WeakReference instead of strong reference. For example, you can use WeakReference<Context> mContextRef;

For details of this part, please refer to the Article section in the Android documentation.

4. It’s all the fault of threads

Threads are also an important source of memory leaks. The main reason for thread memory leaks is that the thread life cycle is uncontrollable. Let's consider the following code.

Some people like to use the AsyncTask provided by Android, but in fact the problem of AsyncTask is more serious. Thread only has this memory leak problem when the run function is not finished. However, the internal implementation mechanism of AsyncTask uses ThreadPoolExcutor. The life cycle of the Thread object generated by this class is uncertain and cannot be controlled by the application. Therefore, if AsyncTask is used as an inner class of Activity, it is more likely to have memory leak problems.

In fact, the problem with threads is not just memory leaks, it can also bring some catastrophic problems.

5. The Cursor's mysterious whereabouts

Cursor is a class for managing data collection obtained after Android queries data. Under normal circumstances, if the amount of data obtained by the query is small, there will be no memory problem, and the virtual machine can ensure that the Cursor will eventually be released.

However, if the amount of data in the Cursor is particularly large, especially if it contains Blob information, the memory occupied by the Cursor should be released in time, rather than waiting for GC to process it. And Android obviously prefers programmers to manually close the Cursor, because we found in the source code that if you wait until the garbage collector comes to recycle it, the user will be given an error prompt.

There is a case where we cannot close the Cursor directly, which is the case when it is applied in CursorAdapter, but note that CursorAdapter does not automatically close the Cursor when Acivity ends, so you need to close it manually in the onDestroy function.

Please indicate the source for reprinting: How to optimize memory in Android development

<<:  LG G Watch R Android smartwatch review

>>:  The arrival of HTML5 era: the official version of HTML5 standard specification is released

Recommend

User operation: How to do detailed user classification?

User operation is a module of knowledge required ...

Adobe is about to bring these heavyweight tools to the iOS platform

This is a golden age of creativity. We believe th...

How much does it cost to apply for a 400 phone number?

How much does it cost to apply for a 400 phone nu...

How much does it cost to develop a Huludao specialty mini program?

The overall investment promotion of Huludao speci...

Analysis of mobile advertising in the skin care and beauty industry in Q1 2019

The first quarter of 2019 has passed. So what are...

Xbox One and PS4 are about to become one family in China

Guided by the establishment of the Shanghai Free T...

Apple adjusts its product line layout: selling 4 iPhone models at the same time

The release of iPhone 6 has made headlines in maj...

Tea Science | Why is tea bitter? The truth is hidden here →

Have you ever encountered a situation where the t...

If "live suicide" happens in the circle of friends

Social relationships on WeChat are closer, so the...