background

Android assigns a process to each App. Android sets a hard limit on the size of the heap that can be allocated to each App. If you try to allocate memory after the maximum heap is reached, An OutOfMemoryError (memory overflow) occurs.

1.1 Obtaining Memory


ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)
activityManager.getMemoryClass();
Copy the code

1.2 Place to set the size

In AndroidRuntime. CPP getMemoryClass is the corresponding heapGrowthLimit value

    parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms"."4m");
    parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx"."16m");

    parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
    parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
    parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
    parseRuntimeOption("dalvik.vm.heaptargetutilization",
                       heaptargetutilizationOptsBuf,
                       "-XX:HeapTargetUtilization=");
Copy the code

1.3 Viewing the Application Heap Memory

Can pass ActivityManager. MemoryInfo object to view the current available memory, maximum memory, and the threshold of low memory

ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
 // Free memory
 memoryInfo.availMem;
 // Low memory threshold
memoryInfo.threshold;
/ / the total memory
memoryInfo.totalMem;
Copy the code

Android memory allocation and reclamation mechanism

2.1 Memory model of the JVM

Methods area

It is used to store information about classes that have been loaded by the virtual machine, constants, static variables, code compiled by the just-in-time compiler, and so on. In JDK1.7 (included), the constant pool was moved from PermGen to Java heap memory. In JDK1.7 (included), the constant pool was moved from PermGen to Java heap memory. 1.8 Replacing permanent generation deletion with metadata

Local method stack

The local method stack is similar to the Java virtual machine stack, which is used to manage calls to Java functions, and the local method stack, which is used to manage calls to local methods. However, the local method is not implemented in Java, but by C language, all versions of the virtual machine implementation, HotSpot directly local method stack and virtual machine stack into one

Java heap is also known as GC heap

It is the largest chunk of memory managed by the Java Virtual machine. The Java heap is an area of memory that is shared by all threads and is created when the virtual machine is started. The only purpose of this memory area is to store object instances. Almost all object instances are allocated memory here. From the point of view of memory collection, the Java heap can also be divided into: new generation and old generation; More detailed examples include Eden space, From Survivor space, To Survivor space, where member variables are placed, and after JDK1.7 (inclusive) the constant pool was moved From PermGen To Java heap memory

The virtual machine stack

The thread is private and has the same life cycle as the thread. Virtual machine Stack describes the memory model of Java method execution. Each method execution creates a stack-frame for storing messages such as local variable table, operand Stack, dynamic link, method exit, etc. The process of each method from invocation to completion corresponds to the process of a stack frame being pushed into and out of the virtual machine stack. Room, also oom

Program counter

This memory region is the only one where the Java Virtual Machine specification does not specify any OutOfMemoryError cases. It can be thought of as a line number indicator of the bytecode being executed by the current thread. Class is decompiled with the Javap command to show the sequence number of each line. It is mainly used to record the location of the current thread. When the thread switches, it will record the next running instruction of the thread. Here to explain why each thread requires a thread counter, the JVM multithreading is done by thread switching in turn allocation execution time, at any time, each processor can only execute one thread in the instructions, when threads to switch to the thread to restore when the correct position, so each thread must have a separate thread counter, This ensures that threads do not interfere with each other

2.2 Object Life cycle

In JVM runtime space, the entire life cycle of an object can be roughly divided into seven phases: Creation stage, Using stage, Invisible stage, Unreachable stage, Collected stage, Finalized stage, and Free stage. These seven phases constitute the complete life cycle of an object in the JVM. The following describes the different situations of objects in each of the seven phases

Creation Stage

The create phase allocates memory for the object, calls constructors, and initializes properties. When an object is created, it is stored in the JVM heap waiting to be used to see if it meets escape analysis, then whether the thread allocation buffer pool is enabled, and then whether it is a large object. According to statistics, 90% of them will be used once. After the new object comes out, it will be recycled during GC. Therefore, new objects are put into Edog first and recycled once, and they may all be recycled

Application stage (Using)

At least one strong reference to an object is said to be in use

Invisible Stage

Reference objects become invisible when they go out of scope, and objects inside a method are invisible outside the method

Unreachable stage

An object in the unreachable phase is no longer held by a GC root reference

Collected And Collected

An object enters the “collection phase” when the garbage collector finds that the object is in the “unreachable phase” and the garbage collector is ready to allocate the object’s memory space again. Assuming that the object has overridden the Finalize () method, the terminal operation of the finalize() method will be run

King of the Royal Academy

When an object is still unreachable after finalize() is run, the object enters the finalization stage. This phase waits for the garbage collector to reclaim the object space.

Release phase or reuse (Free)

When the garbage collector collects or reallocates the memory space occupied by the object, the object disappears completely, which is called “object space allocation stage again”.

2.2 Can act as GC root

  • Objects referenced in the virtual machine stack (the local variable table in the stack frame).

  • The object referenced by the static class attribute in the method area.

  • The object referenced by the constant in the method area.

  • Objects referenced by JNI (commonly referred to as Native methods) in the Native method stack

2.3 Garbage collection algorithm

For details on GC, see Android GC principles

Mark and Sweep GC

For old age, permanent generation, (scan both sides, mark first, clear second) mark the space, then clear, memory space is not continuous, so when there are 12 address values,

  • Let’s say 90% of this needs to be recycled, that’s too much trouble, let’s say 10% is better so, mark clearance is good for older generations and permanent generations
  • Memory space is not contiguous, that is, memory fragmentation

Mark-compact algorithm

For old age, permanent generation, you need to mark all reachable objects starting at the root node, but after that, instead of simply cleaning up the unmarked objects, it compresses all living objects to one end of memory. After that, clean up all the space outside the boundary. This method not only avoids fragmentation, but also does not require two pieces of the same memory space, so it is cost-effective.

  • Object movement is involved, address references need to be updated, and user threads need to be suspended, resulting in low overall efficiency

Replication algorithm

New generation: It divides the available memory by capacity into two equal chunks, one of which is used at a time. When this area of memory is used up, the surviving objects are copied to the other area, and the used memory space is cleaned up again

  • Efficiency problem: When the object survival rate is high, the number of replication operations is more, and the efficiency is reduced.
  • Space issues: Memory has shrunk by half; This is standard, but the JVM also has an Eden region, which used to be 50% utilization at 8:1:1. Now it has an Eden region, and a FROM region, which equals 90% utilization

2.4 GC of Dalvik VM

Most of The mainstream Davik uses The Mark and Sweep recovery algorithm, and some implement copy-GC, which is different from HotSpot. The Dalvik VIRTUAL machine’s GC stops The World, at which point The whole program thread will hang. All threads inside the virtual machine are suspended at the same time, waiting for the GC to finish. GC, also known as STW, is frequently performed when memory is tight, resulting in frame loss and interface lag

2.5 ART of GC

The Java heap used internally by ART runtime mainly consists of Image Space, Zygote Space, Allocation Space and Large Object Space. Image Space is used to store some preloaded classes. The functions of Zygote Space and Allocation Space are the same as those of Zygote heap and Active heap in Dalvik VM garbage collection mechanism. Unlike Dalvik, Art has only one garbage collection algorithm in GC. Art will choose different garbage collection algorithms in different situations. For example, non-concurrent GC is used when Alloc memory is low, and concurrent GC is triggered when Alloc memory reaches a certain threshold. GC strategies are also different in both front and back scenarios. In The background GC, The World is not stopped. The efficiency of Art memory allocation is increased by 10 times compared with Dalvik, and The efficiency of GC is increased by 2-3 times.

Three process priorities

When the phone can’t free enough memory for the system. In this case, the system uses onTrimMemory() to inform the application that it is out of memory and should reduce its allocation. If this is not enough, the kernel starts terminating the process to free up memory. It uses the low memory termination daemon (LMK) to perform this operation

  • Background application: A previously running application that is not currently active. LMK will terminate the background application first, starting with the application with the highest OOM_ADJ_score. The termination message is as follows

  • Previous application: Recently used background application. The previous app has a higher priority (lower score) than the background app because the user is more likely to switch to the previous app than to a background app.

  • Home screen app: This is the launcher app. Terminating the application causes the wallpaper to disappear.

  • Services: Services are started by the application and may include synchronization or uploading to the cloud.

  • Perceptible applications: Non-foreground applications that the user can detect in some way, such as running a search process that displays a small interface or listening to music.

  • Foreground application: the application currently in use. Terminating a foreground app looks like the app crashed and may alert the user that there is a problem with the device.

  • Persistence (services) : These are the core services of the device, such as telephony and WLAN.

  • System: a system process. After these processes are terminated, the phone may appear to be about to restart.

  • Native: a very low-level process used by the system (for example, KSWAPd)

Memory leakage and check

4.1 Memory leak scenarios

  1. The resource object is not closed

Close File, Cursor, Stream when not in use 2. If there is a single benefit that needs to be passed to the Context, it is better to use the Application rather than the Activity or Service. Non-static inner classes automatically hold references to external classes, and static instances created will always hold references. Consider declaring inner classes static

  1. Handlers are the same as above. Non-static handlers refer to references to external classes, which in the case of static handlers will result in a string of static attributes. So remember RxJava

  2. Objects that are not de-registered, such as BroadCastReciver, EventBus, and Disposable dispose of RxJava

  3. WebView leak, some people’s solution is to open a new process, but I don’t see that several apps have opened a new process.

  4. Memory leaks caused by collection objects not being cleaned up in time

  5. Memory leaks caused by threads and Runable

4.2 Detecting Memory Leaks

2 MAT

The official documentation

4.2.2 Android Studio Profiler

Official document

Holdings LeakCanary

The first two are visual, and in general, we use LeakCanary to detect memory leaks as follows

  1. Initialization: The debugImplementation is implemented directly. It’s done inside the ContentProvider. When it’s packaged, it merges the manifest files. The registered ContentProvider is created after the attachBaseContext of the Application and before the onCreate. When the ContentProvider thing happened,
  2. Reference queues can be used with soft, weak, and virtual references, and referenced objects are added to the reference queue when they are to be reclaimed by the JVM.
  3. The registration Application. ActivityLifecycleCallbacks to monitor the Activity of life cycle, And fragmentManager registerFragmentLifecycleCallbacks listening fragments of life cycle.
  4. Put the Activity in the observation array and wrap the Activity with a reference queue. Generate a key (UUID). After 5 seconds, check whether the key is in the reference queue. If count > 0 in the array, there is a suspected leak. Then call Runtime.geTruntime ().gc(). I’m going to remove that key from the observation array, and then I’m going to look at the count in the observation array, and if it’s less than 5, just a hint. If count is greater than 5 (to prevent stalling), shark (before 2.0, haha) is used to analyze stack information. Using accessibility analysis, find the shortest link,

Reference types and reference queues

We know that in Java, all but the underlying data types are reference types. Java divides reference types into strong reference, soft reference, weak reference and virtual reference according to the length of their life cycle

5.1 Reference Types

Strong reference

Strong references are the most commonly used references. If an object has a strong reference, the JVM would rather throw OOM than reclaim it

Soft references

If an object has only soft references, the garbage collector will not reclaim it when there is enough memory; If you run out of memory, the objects are reclaimed. As long as the garbage collector does not collect it, the object can be used by the program

A weak reference

The difference between weak and soft references is that objects with only weak references have a shorter lifetime. When the garbage collector thread scans the memory area under its control, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient. However, because the garbage collector is a low-priority thread, objects that have only weak references are not necessarily found quickly

Phantom reference

No reference

5.2 Reference Queue

Reference queues can work with soft, weak, and ghost references, and are added to the reference queue when the referenced object is about to be recycled by the JVM. LeakCanary uses this principle to detect leaks in activities or fragments

Turn on strict mode

You can set StrictMode to listen for potential problems, print a log when a problem occurs, or simply throw an exception.

public void onCreate() {
     if (DEVELOPER_MODE) {
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog()
                 .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
}
Copy the code

Six OOM and find OOM

In fact, Android Bitmap processing is good, basically will not OOM, Bitmap is a major play, of course, in the actual project we usually use Glide can be done

6.1 What are the causes of OOM

  1. The Java heap is out of memory
  2. The heap has too many memory fragments, resulting in a slightly larger OOM
  3. Too many FB handles
  4. Too many threads (I created 1600 threads on Huawei Honor 30 and got OOM)
  5. Virtual inadequacy
  6. Memory jitter causes frequent GC, interface delay, or OOM

6.2 Avoiding OOM Solutions

6.2.1 Release resources when Listening for low memory

You can be in the Activity. OnTrimMemory () Fragement. OnTrimMemory () Service. OnTrimMemory () ContentProvider. OnTrimMemory () implementation in the class ComponentCallbacks2 interface, or override onTrimMemory method in Application, listen for low memory callback, do the corresponding release callback official documentation

6.2.2 Use more lightweight data structures

The memory efficiency of a regular HashMap implementation can be quite low, since each map needs to correspond to a separate item object. We can consider using ArrayMap/SparseArray instead of traditional data structures such as HashMap. SparseArray classes are more efficient because they avoid the need for the system to automatically box keys (and sometimes values), such as ArrayMap swapping time for space, We can consider using ArrayMap with binary lookup, which is not as fast as HashMap, but has better performance than HashMap. Google recommends using ArrayMap for up to 1000 users. SparseArray, SparseBooleanArray, LongSparseArray, using these apis to make our application more efficient, it eliminates the automatic boxing function

6.2.3 RecommendedMMKVOr is itDataStoreUsed instead of SharePreference

MMKV and DataStore use Protobuf for data serialization, SharePreferenc uses Xml, Protocol Buffers performance is better than Xml, MMKV principle uses MMap(memory map), copy data once, can also be changed locally. However, SharePreferenc uses traditional IO operations to copy data twice without local changes. Each change will be written to the entire XML. Both commit and apply will cause ARN to analyze ANR problems caused by SharedPreference apply

6.2.4 Protobuf is recommended for serialized data

Protobuf is a language – and platform-independent, extensible mechanism designed by Google for serializing structured data. This mechanism is similar to XML, but smaller, faster, and simpler. If you decide to use Protobuf for data, you should always use a compact version of Protobuf in your client code. Conventional Protobuf generates extremely lengthy code, which can cause problems with applications such as increased RAM usage, significantly increased APK size, and slower execution.

6.2.5 Avoiding Memory Jitter

Normal GC has no impact on performance and can quickly run out of frame time if many garbage collection events occur in a short period of time. The more time a system spends on garbage collection, The less time it can spend on other tasks such as rendering or streaming audio, with frequent GC having Stop The World operations. It can cause lag, memory fragmentation, etc. Avoid allocating multiple temporary objects in a large number of for and reduce initialization of objects in onDraw().

6.2.6 Avoid enumerations

Enumerations make the class larger, and you’d rather write constants in your project with the IntDef annotation than enumerations

6.2.7 Avoid direct new threads () and use Thread pools or coroutines instead

Thread is a very valuable thing in Android, he takes up a lot more resources than ordinary classes, I then Huawei honor 30 mobile phone, created 1600 threads, OOM

6.3 Use of Bitmap

In fact, this is a major play, android as long as the Bitmap processing, basically not OOM, when we generally use Glide in the project basically done, but also say a few words.

Bitmap format

  • Argb_8888: each pixel has 32 bits, a=8; r=8; g=8; b=8; A total of 8*4=32 bits, a byte 8 bits, a total of four bytes;

  • Argb_4444: each pixel is 16 bits, 2 bytes;

  • Rgb_565: each pixel is 16 bits, 2 bytes; G represents 6 invalid reservations,

  • Alpha_8: Each pixel occupies 8 bits and 1 byte;

How much memory Bitmap takes up

Bitmap has an Api called getByteCount(). For details, see the Android pit file: How much memory does your Bitmap take up?

It’s on an SD card or the Internet

If 500×500 images are stored on an SD card or downloaded from the Internet, the algorithm is as follows when using argb_8888: 500x500x4 = 1000000 B = 1000000/1024/1024 = 0.95m rag_565 is half smaller than argb_8888

In drawable-xxdpi,

Drawable (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888) : argb_8888 (argb_8888

Length/folder density x mobile phone density x Width/folder density x mobile phone density x 4= (500/480 x 440) x (500/480 x 440) x 4= 840277 B = 0.8MCopy the code

You can also remember length * width * (folder density/real phone density) * image format

Bitmap compression

Calculate or see Tencent article Android picture compression analysis

Plugin for large graph detection

DoraemonKit Hunter

Refer to the link

In the second quarter of this year, we will optimize the performance of the OOM on Android. In the second quarter, we will optimize the performance of the OOM on Android. How much memory does your Bitmap take up?