• As a personal study note, please verify carefully.

Memory management overview: developer.android.com/topic/perfo… Memory allocation: developer.android.com/topic/perfo… Android Studio Profiler:developer.android.com/studio/prof… Dalvik-heap:cs.android.com/android/pla… Blog.csdn.net/whbing1471/… The Android system memory pages: developer.android.com/topic/perfo…

An overview of memory management

The Android Runtime (ART) and Dalvik virtual machines use paging and memory mapping to manage memory.

1.1 Memory Type

Android devices contain three different types of memory: RAM, zRAM, and memory. Note that the CPU and GPU access the same RAM.

  • RAM (Random Access Memory) is the fastest type of Memory, but its size is usually limited.
  • ZRAM is the RAM partition used for swap space. All data is compressed as it is put into zRAM and then decompressed as it is copied out of zRAM. This portion of RAM grows or shrinks as pages enter and exit zRAM. Device manufacturers can set a zRAM size upper limit.
  • Storage contains all persistent data (such as file systems, etc.), as well as object code added for all applications, libraries, and platforms. On Android, memory is not used for swap space as it is on other Linux implementations, because frequent writes can corrupt this memory and shorten the life of the storage medium.

1.2 Physical Memory Virtual memory

1.2.1 Physical Memory

Physical memory refers to the capacity of the memory bar inserted in the memory slot on the mainboard. It is the storage space shared by all processes in the Android system.

1.2.2 Virtual Memory

When a process applies for memory, it gets virtual memory, which is an abstraction of physical memory. Each process operates on its own virtual memory and the CPU is responsible for mapping the virtual memory address to the actual physical address.

Note that if the process is not using this virtual memory, no mapping of virtual addresses to physical memory will be established. Such as:

void main(a)
{
    for (int i= 0; i < 512; i++)
    {
        malloc(1024 * 1024); }}// The virtual memory size of the process is 539M, while the physical memory size is 3M.
Copy the code

The mapping between virtual memory and physical memory is established only when the process actually uses memory. For example:

vector<void*> mems;
void main(a)
{
    for (int i= 0; i < 512; i++)
    {
        void *p = malloc(1024 * 1024);
        memset(p, 0.1024 * 1024);
        mems.push_back(p); }}// The virtual memory size of the process is 539M, while the physical memory size is 527M.
Copy the code

1.2.3 Benefits of virtual memory

(1) The application does not have to manage the shared memory space; Because all processes share physical memory, physical memory is often discontinuous, whereas for applications, virtual memory addresses are contiguous, and the CPU is responsible for mapping virtual memory addresses. (2) Memory used by libraries can be shared between processes; See the pathogen; (3) Improved security due to memory isolation; (4) More memory can be used than physical memory size by using paging or segmentation techniques.

1.2.4 Shared library memory

Memory occupied by shared library: The code of a shared library exists only one copy in physical memory, which is mapped to the virtual memory of different processes. For each process, it is like its own private memory, but for the system, it saves memory resources. The Android system implements RAM page sharing across processes in the following ways:

  • Each application process forks from an existing process called Zygote. The Zygote process starts when the system starts and loads the generic framework code and resources, such as the Activity theme background. To start a new application process, the system forks the Zygote process and loads and runs the application code in the new process. This approach enables most OF the RAM pages allocated for framework code and resources to be shared across all application processes.
  • Most static data is memory-mapped to a process. This approach allows data not only to be shared between processes, but also swapped out as needed. Examples of static data include: Dalvik code (by putting it into a pre-linked.odexDirect memory mapping in files), applying resources (by designing resource tables into memory-mappable structures and by aligning ZIP entries in APK), and traditional project elements (e.g.soNative code in files).
  • In many places, Android uses explicitly allocated shared memory areas (via Ashmem or Gralloc) to share the same dynamic RAM between processes. For example, the window Surface uses memory shared between the application and the screen synthesizer, while the cursor buffer uses memory shared between the content provider and the client.

1.3 VSS RSS PSS USS difference

The memory information of a process can be expressed as VSS RSS PSS USS, with the following meanings:

Virtual Set Size (VSS) : indicates the Virtual memory Size (VSZ is used in the ps command).

Resident Set Size (RSS) : Resident memory Size and the number of shared and unshared pages used by applications.

Proportional Set Size (PSS) : Proportional memory Size (non-shared memory used by applications) and Proportional number of shared memory (for example, if three processes share 3MB, the PSS of each process is 1MB).

USS (Unique Set Size) : physical memory exclusively used by processes (excluding memory occupied by shared libraries).

The shared library libtest. so occupies 100 MB of memory, which is shared by processes A and B. (2) Process A requested 100M memory, but actually used 60M memory; Then, VSS of process A =100M+100M=200M; RSS=60M+100M=150M; PSS=60M+100/2=110M; USS=60M;Copy the code

So, if you want to know how much memory all processes are using, you can use PSS or RSS. Calculating PSS takes a long time because the system needs to determine which pages are shared and how many processes are sharing the pages. RSS does not distinguish between shared and unshared pages (and is therefore faster to calculate) and is better for tracking changes in memory allocation.

1.4 Page replacement of Android system

RAM is divided into multiple “pages.” Typically, each page has 4KB of memory.

  • 1. An unmodified copy of a file in storage;
  • Dirty page: a modified copy of a file in storage.

On Android, ROM storage is not used for swap space as it is on other Linux implementations, because frequent writes can corrupt the ROM and shorten the life of the storage medium. The Kernel swap daemon (KSWAPD) is part of the Linux kernel that converts used memory to available memory. When the available memory on the device is low, the daemon becomes active. The Linux kernel has upper and lower thresholds for free memory. When the available memory falls below the lower threshold, kSWAPD starts to reclaim the memory. When the available memory reaches the upper threshold, KSWAPD stops reclaiming memory. The Android system uses zRAM for swap space, and private dirty pages can be moved to zRAM by KSWAPD/compressed in zRAM to increase available memory, and then extracted from zRAM/ decompressed in zRAM when the application needs to use this part of memory. See: Memory page replacement policy

Second, memory analysis method

2.1 ADB Common memory analysis commands

2.1.1 the ps command

The ps command can list the information about all processes in the Android system:

>adb shell psUSER PID PPID VSZ RSS WCHAN ADDR S NAME root 1 0 60588 1360 0 0 S init root 2 0 60588 1360 0 0 S [kthreadd] root 144 2 0  0 0 0 S [kswapd0] root 1034 1 4286332 22916 0 0 S zygote64 root 1035 1 1616152 25532 0 0 S zygote system 570 1 11356 1196 0 0 S servicemanager system 1655 958 4786828 277192 0 0 S system_server u0_a1166 9742 1035 1716624 20 0 0 T com.bc.sample root 727 1 9968 2204 0 0 S lmkd
>adb shell ps | grep com.bc.sample
u0_a1166      9742  1035 1716624     20 0                   0 T com.bc.sample
Copy the code

USER: indicates the USER to which a process belongs. PID: indicates the ID of a process. PPID: indicates the ID of the parent process. VSZ: Virtual Memory Size Virtual Memory Size, unit: KB RSS: Resident Set Size Physical Memory Size, unit: KB NAME: process NAME.

2.1.2 dumpsys meminfo

The dumpsys meminfo command can obtain the current memory usage of a process based on the process name (usually the package name) :

#Command line input
>adb shell dumpsys meminfo com.bc.sampleApplications Memory Usage (in Kilobytes): Uptime: 654784890 Realtime: 2062282674 ** MEMINFO in pid 13705 [com.bc.sample] ** Pss Private Private SwapPss Heap Heap Heap Total Dirty Clean Dirty  Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 3445 3396 0 50 7680 6140 1539 Dalvik Heap 667 612 0 68 2671 1135 1536 Dalvik Other 360 348 0 0 Stack 48 48 0 0 Ashmem 2 0 0 0 Gfx dev 1664 1576 88 0 Other dev 12 0 12 0 .so mmap 1528 180 0 31 .apk mmap 166 0 28 0 .ttf mmap 58 0 0 0 .dex mmap 3834 4 1744 0 .oat mmap 102 0 0 0 .art mmap 3368 3036 4 7 Other mmap 13 4 0 0 EGL mtrack 18496 18496 0 0 GL mtrack 14964 14964 0 0 Unknown 473 464 0 6 TOTAL 49362 43128 1876 162 10351 7275 3075 App Summary Pss(KB) ------ Java Heap: 3652 Native Heap: 3396 Code: 1956 Stack: 48 Graphics: 35124 Private Other: 828 System: 4358 TOTAL: 49362 TOTAL SWAP PSS: 162 Objects Views: 22 ViewRootImpl: 2 AppContexts: 3 Activities: 1 Assets: 3 AssetManagers: 4 Local Binders: 15 Proxy Binders: 21 Parcel memory: 3 Parcel count: 13 Death Recipients: 2 OpenSSL Sockets: 0 WebViews: 0 Dalvik isLargeHeap: false SQL MEMORY_USED: 0 PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0Copy the code

Adb shell cat /proc/meminfo

The /proc/meminfo file can be read to obtain the overall memory usage of the system:

>adb shell cat /proc/meminfoMemTotal: 5826364 kB // Memory size MemFree: 85532 kB // Memory size MemAvailable: 3540368 kB Buffers: 127840 kB Cached: 3373548 kB SwapCached: 10952 kB Active: 2928196 kB Inactive: 1346956 kB Active(anon): 427512 kB Inactive(anon): 432012 kB Active(file): 2500684 kB Inactive(file): 914944 kB Unevictable: 81280 kB Mlocked: 81280 kB SwapTotal: 2097148 kB // Total size of swap space SwapFree: 1449580 kB // Free size of swap spaceCopy the code

2.1.4 procrank

The procrank command can get the VSS, RSS, PSS, USS sizes for all processes in the system:

>adb shell procrank
PID Vss      Rss     Pss     Uss    cmdline
380 2195880K 210712K 111133K 67400K system_server
146 1561656K 63080  47628K  42036K zygote
868 1587984K 69500K  25414K  18480K com.android.launcher
145 2122596K 73552K  23428K  10972K zygote64
Copy the code

2.2 the Android Studio Profiler

Android Studio’s built-in profiler allows you to select MEMORY to view current MEMORY usage, as shown below:The memory usage of the selected process is listed, where the Java Heap and native heap refer to the virtual memory size:

2.3 Code Acquisition

2.3.1 Total Mobile Phone memory MemoryInfo

MemoryInfo contains the following memory information:

public static class MemoryInfo implements Parcelable {

    public long availMem;
    public long totalMem;
    public long threshold;
    public boolean lowMemory;
}
Copy the code

MemoryInfo can be obtained as follows:

// You can get activityManager from a custom class in this way:
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);

Log.v(TAG,"Total phone memory :" + memoryInfo.totalMem);
Log.v(TAG,"Current available memory of the phone :" + memoryInfo.availMem);
Log.v(TAG,"The threshold for low memory status considered by Android :" + memoryInfo.threshold);
Copy the code

2.3.2 Reading the /proc/meminfo file

The total memory of the system can also be obtained by reading /proc/meminfo. See 2.1.3 for the available information. The code is as follows:

public static long getRamSwapFree(a) {
    // // System memory information file
    String str1 = "/proc/meminfo"; 
    String str2;
    String[] arrayOfString;
    long swapFree = 0;
    InputStreamReader localFileReader = null;
    BufferedReader localBufferedReader = null;
    try {
        localFileReader = new InputStreamReader(new FileInputStream(str1), Charset.forName("utf-8"));
        localBufferedReader = new BufferedReader(localFileReader, 8192);
        while((str2 = localBufferedReader.readLine()) ! =null) {
            arrayOfString = str2.split("\s+");
            if (arrayOfString.length >= 2) {
                String memType = arrayOfString[0];
                // Here is an example, similarly for more information
                if (TextUtils.equals(arrayOfString[0]."SwapFree:")) {
                    swapFree = Integer.parseInt(arrayOfString[1]); }}}}catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (null! = localBufferedReader) {try {
                localBufferedReader.close();
            } catch(IOException e) { e.printStackTrace(); }}if (null! = localFileReader) {try {
                localFileReader.close();
            } catch(IOException e) { e.printStackTrace(); }}}return swapFree / (1024);
}
Copy the code

2.3.3 Obtaining the PSS of the Current process

Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memoryInfo);
Log.v(TAG,"Total PSS."+memoryInfo.getTotalPss());
Log.v(TAG,"Java PSS:" + memoryInfo.dalvikPss);
Log.v(TAG,"Native PSS:" + memoryInfo.nativePss);
Log.v(TAG,"Total RSS."+memoryInfo.getTotalRss());

// Android 6.0 can also use the following methods:
Log.v(TAG,"Android 6.0 +, Total-PSS :"+memoryInfo.getMemoryStat("summary.total-pss"));
Log.v(TAG,"Android 6.0 +, Java-heap :"+memoryInfo.getMemoryStat("summary.java-heap"));
Log.v(TAG,"Android 6.0 +, native heap:"+memoryInfo.getMemoryStat("summary.native-heap"));
Log.v(TAG,"Android 6.0 or above, code:"+memoryInfo.getMemoryStat("summary.code"));
Log.v(TAG,"Android 6.0 +, graphics:"+memoryInfo.getMemoryStat("summary.graphics"));
Copy the code

2.3.4 Obtaining the VSS of the Current Process

(1) Obtain the Java heap VSS

long javaMaxHeapSize = Runtime.getRuntime().maxMemory();
long javaTotalHeapSize = Runtime.getRuntime().totalMemory();
long javaFreeHeapSize = Runtime.getRuntime().freeMemory();
Log.v(TAG,"Maximum Java heap memory size :"+javaMaxHeapSize);
Log.v(TAG,"Current Java heap size :"+javaTotalHeapSize);
Log.v(TAG,"Current Java heap free memory size :"+javaFreeHeapSize);
Copy the code
  • MaxMemory is set during VM startup. The default value is set according to the RAM size (192M (2G RAM), 192M (4G RAM), 256M (6G RAM), but also depends on the specific machine configuration), or 512M if largeHeap is enabled.
  • TotalMemory is the current heap size, which can be increased up to maxMemory.

(2) Obtain the NATIVE heap VSS

long nativeHeap = Debug.getNativeHeapSize();
long nativeAllocHeap = Debug.getNativeHeapAllocatedSize();
long nativeFreeSize = Debug.getNativeHeapFreeSize();
Log.v(TAG,"Current native heap size :"+nativeHeap);
Log.v(TAG,"Current native heap allocated memory size :"+nativeAllocHeap);
Log.v(TAG,"Current native heap free memory size :"+nativeFreeSize);
Copy the code
  • The growth of the Native heap is not limited by the Dalvik VM Heapsize. Applications can always apply for space on the Native heap as long as there is free space in virtual memory (see Reference).

Low memory status

3.1 LMK process

A lot of times,kswapdUnable to free enough memory for the system. In this case, the system will useonTrimMemory()Notifies 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.

LMK uses a name namedoom_adj_scoreTo determine the priority of the running process and thus the process to terminate. The process with the highest score is terminated first. Background applications are terminated first and system processes are terminated last. The following table lists the LMK score categories from high to low. The category with the highest rating, the item in the first row, will be terminated first (the device manufacturer can change the behavior of LMK). :

Below is a description of the various categories in the table above:

  • Background application: A previously running application that is not currently active. LMK will be first from having the highestoom_adj_scoreThe application starts to terminate the background application.
  • 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).

3.2 onTrimMemory ()

As described above, onTrimMemory() is called back to notify the application that it is out of memory when it is out of memory. OnTrimMemory () is defined as follows:

public interface ComponentCallbacks2 extends ComponentCallbacks {
    void onTrimMemory(@TrimMemoryLevel int level);
}
Copy the code

For Application, Activity, Fragment, Service, ContentProvider, source code has inherited the ComponentCallbacks2 interface, If the developer needs to customize more of onTrimMemory’s behavior (such as doing some memory free work when receiving out-of-memory callbacks), it can simply override the parent class’s methods. If a developer needs to receive ComponentCallbacks2 system callbacks in a custom class, register as follows:

public class ComponentCallbacksDemo implements ComponentCallbacks2{
    
    public ComponentCallbacksDemo(a) {
        // Register the ComponentCallbacks callback
        getApplicationContext().registerComponentCallbacks(this);
    }
    
    @Override
    public void onTrimMemory(int level) {
        if (level >= TRIM_MEMORY_UI_HIDDEN) {
            // todo do some free memory work}}@Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {}
    
    @Override
    public void onLowMemory(a) {}}Copy the code

When onTrimMemory(int level) is called back, level indicates the memory level. There are several types of level:

/**
 * Level for {@link #onTrimMemory(int)}: the process is nearing the end
 * of the background LRU list, and if more memory isn't found soon it will
 * be killed.
 */
static final int TRIM_MEMORY_COMPLETE = 80;

/**
 * Level for {@link #onTrimMemory(int)}: the process is around the middle
 * of the background LRU list; freeing memory can help the system keep
 * other processes running later in the list for better overall performance.
 */
static final int TRIM_MEMORY_MODERATE = 60;

/**
 * Level for {@link #onTrimMemory(int)}: the process has gone on to the
 * LRU list.  This is a good opportunity to clean up resources that can
 * efficiently and quickly be re-built if the user returns to the app.
 */
static final int TRIM_MEMORY_BACKGROUND = 40;

/**
 * Level for {@link#onTrimMemory(int)}: the process had been showing * a user interface, and is no longer doing so. Large allocations with * the UI should be released at this point to allow memory to be better  * managed. */
static final int TRIM_MEMORY_UI_HIDDEN = 20;

/**
 * Level for {@link #onTrimMemory(int)}: the process is not an expendable
 * background process, but the device is running extremely low on memory
 * and is about to not be able to keep any background processes running.
 * Your running process should free up as many non-critical resources as it
 * can to allow that memory to be used elsewhere.  The next thing that
 * will happen after this is {@link #onLowMemory()} called to report that
 * nothing at all can be kept in the background, a situation that can start
 * to notably impact the user.
 */
static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;

/**
 * Level for {@link#onTrimMemory(int)}: the process is not an expendable * background process, but the device is running low on memory. * Your running process should free up unneeded resources to allow that * memory  to be used elsewhere. */
static final int TRIM_MEMORY_RUNNING_LOW = 10;

/**
 * Level for {@link #onTrimMemory(int)}: the process is not an expendable
 * background process, but the device is running moderately low on memory.
 * Your running process may want to release some unneeded resources for
 * use elsewhere.
 */
static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
Copy the code

3.2 onLowMemory ()

OnTrimMemory is available for API 14 and above, and onLowMemory() is available for low systems, which is equivalent to ComponentCallbacks2 when onTrimMemory(TRIM_MEMORY_COMPLETE} callback, NTrimMemory () is recommended for high systems.

public interface ComponentCallbacks {
    void onConfigurationChanged(@NonNull Configuration newConfig);
    void onLowMemory(a);
}
Copy the code