preface

Memory is a common problem, but it’s not getting enough attention for several reasons. 1. Memory issues are relatively subtle and not obvious 2. Android is also developed using the Jvm language and garbage collection is automatic, so there is generally no special concern 3. Memory problems are hard to locate, and often the problem is just a manifestation of the problem, and the real cause is hard to gather

The content of memory optimization is actually very complicated. We can try to understand 1 from the following ideas. To understand the memory problem, we first need to understand why memory optimization? 2. You also need to know some background knowledge about memory optimization, such as garbage collection 3. We need to understand some common tools and means of memory optimization 4. Picture is the focus of memory optimization, we need to focus on the knowledge of picture optimization 5. A direct embodiment of the memory problem is OOM. We also need to understand some means of OOM governance

So we can easily get to the main points of this article: 1. Why memory optimization? 2. Some background knowledge of Android memory optimization 3. Common tools and means of Android memory optimization 4. How to do picture memory optimization? 5. How to do OOM online monitoring?

The main contents of this paper are as follows:

1. Why memory optimization?

To answer this question, we should first clarify the requirements, what we are doing when we do memory optimization. The purpose of memory optimization is to reduce OOM rates, reduce lag, and increase application lifetime.

1.1 to reduceOOMThe rate of

A common reason for memory optimization is to reduce the OOM rate by applying too much memory and not releasing it in time, which often leads to OOM. There are various reasons for OOM, which will be discussed in detail later

1.2 Reduce congestion

There are a number of reasons why an Android interface can get stuck, one of which is memory. Memory problems affect interface fluency because of garbage collection. During GC, all threads are stopped, including the main thread. When both GC and drawing are triggered at the same time, the execution of the drawing is put on hold, causing frames to drop and the interface to get stuck.

1.3 Increasing the application lifetime

If an App is running in the background and using more memory, it will be cleared first. We usually want apps to survive as long as possible, so we should release memory as soon as it is no longer used

2. androidSome background on memory optimization

2.1 JavaGarbage collection mechanism

Java memory reclamation mainly includes the following contents: 1. Reachability analysis algorithm for determining whether objects are reclaimed 2. The Java garbage collection (GC) algorithm is used to collect garbage from the Java database. The Java garbage collection (GC) algorithm is used to collect garbage from the Java database

2.2 What is a Memory leak?

A memory leak is a block of memory that is not being used and cannot be usedGCReclaim, resulting in a waste of memory, for exampleHandlerAnonymous inner class holdsActivityThe reference,ActivityWhen it needs to be destroyed,GCYou can’t recycle it.

A memory leak is a gradual decrease in available memory and accumulation of memory that cannot be reclaimed until the application has no more available memory to apply for, resulting in memory overflow



The immediate cause of a memory leak is that a long-life object references a short-life object, making the short-life object unrecyclable

Common causes of memory leaks are:

1. Non-static inner classes hold external references

2. Static variables are heldcontextA reference to the

3. Resources are not released in time

We typically use LeakCanary or Profile to detect memory leaks

2.3 What Is Memory Jitter?

Memory jitter occurs when we frequently create a large number of temporary objects in a short period of time, such as in aforThe image below is a representation of memory jitter. Its shape is zigzag, and the garbage can in the center represents a timeGC.

Memory jitter means frequent object creation and collection, which can easily trigger GC, and all threads will stop during GC, which can cause stutter. To avoid memory jitter, we should avoid the following operations 1. Try not to create objects in the body of the loop. 2. Try not to create objects in the onDraw() method of a custom View, as this method will be called frequently 3. For objects that can be reused, consider using object pools to cache them

2.4 What is Memory Overflow?

Memory overflow Indicates that the requested memory exceeds the available memoryOOM, which will cause our program to exit abnormally, which is also our focus indicator

causeOOMThere may be a variety of reasons, which can be divided into the following categories



aboutOOMManagement and online monitoring, etc., will be introduced in detail later

3. androidCommon tools and means of memory optimization

3.1 Memory Profiler

Memory ProfilerisProfilerIn one of the sections,ProfilerAndroid StudioProvides us with performance analysis tools to useProfilerAnalytical applicationsCPU, memory, network, and power usage.

useMemoryThe following functions can be tested

1. View the memory curve and memory usage

2. Locate memory jitter

3. Heap dump (Dump Java Heap) objects that can detect memory leaks



aboutMemory ProfilerThe specific use of is not described in this affix, want to know can refer to:What is a Memory Profiler?

3.2 Memory Analyzer Tool

The MAT tool can help developers locate objects causing memory leaks and find large memory objects, then solve memory leaks and reduce memory consumption by optimizing memory objects. MAT is more difficult to use than Memory profilers, and Memory profilers are becoming more powerful, so I don’t use MAT anymore. If you want to learn more about MAT, you can also see: What is The Memory Analyzer Tool

3.3 LeakCanaryDetecting memory leaks

Compared with theMemory ProfilerwithMAT.LeakCanaryMore convenient in use

It’s easy to use to automatically detect memory leaks and alarm by simply adding dependencies to your project

When a leak occurs, the chain of references is as follows:

LeakCanaryIt has the following characteristics:

1. Manual initialization is not required

2. Can automatically detect memory leaks and alarm by notification

3. Cannot be used online

LeakCanaryThe detection process is as follows:

I have previously summarized the principle of LeakCanary in an article that anyone interested in LeakCanary can refer to: Learnwith Questions what you should know about LeakCanary2.0

3.4 Some general means of memory optimization

Some of the details of memory optimization can be avoided during development. Here are some general methods of memory optimization: 1) increase the maximum available memory by using the LargeHeap attribute; 2) remove the cache when the system triggers a resource stress callback; 3) use the optimized collection: 4) Carefully use SharedPreference,SP will load all contents into memory when the application is initialized, so it should not store large contents. 5) Carefully use external libraries, 6) Business architecture design should be reasonable. Abstraction can optimize the flexibility and maintainability of code, but it will also bring other costs, so it should be used in a balanced manner

These details are in fact very common, if you usually notice, I believe that the application of memory must be helpful

4. How to optimize image memory?

Memory optimization should give priority to where the effect is quick. Picture memory optimization is the focus of memory optimization. A picture may not be recycled and cause a waste of several meters of memory

4.1 Conventional image memory optimization methods

As we all know, image memory = wide high pixel occupied memory so optimize picture memory mainly have the following ideas 1. Zoom reduces the width and height. 2. Reduce the memory occupied by each pixel. 3. For large graphs, a strategy of partial loading can be adopted

4.1.1 Reduce image width and height

InSampleSize sometimes the image is 200 × 200 and the View is 100 × 100. In this case, if it doesn’t make sense to show 200 × 200 images, you should scale the image

BitampFactory.Options options = new BitmapFactory.Options();
// Set it to 4 to 1/4 the width and height of the image
options.inSampleSize = 4;
BitmapFactory.decodeSream(is, null, options);
Copy the code

4.1.2 Reduce the memory occupied by each pixel

In API29, Bitmap is divided into six levels: ALPHA_8, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16 and HARDWARE.

  • ALPHA_8: Does not store color information, each pixel occupies 1 byte;
  • RGB_565: only storesRGBChannel, 2 bytes per pixel, rightBitmapColor is not very demanding, can use this mode;
  • ARGB_4444To be abandoned or usedARGB_8888Instead of;
  • ARGB_8888: Each pixel occupies 4 bytes and maintains high quality color fidelity. This mode is used by default.
  • RGBA_F16: Each pixel occupies 8 bytes, suitable for wide gamut andHDR;
  • HARDWARE: a special configuration to reduce memory footprint and speed upBitmapIn the drawing.

Each level also takes up different bytes per pixel and stores different color information. For the same 100 pixel image, ARGB_8888 takes up 400 bytes and RGB_565 only takes up 200 bytes, so in some scenarios, changing the image format can reduce memory by half

4.1.3 Memory overcommitment to avoid repeated memory allocation

Bitmap occupies a large amount of memory. If we create and recycle Bitmap frequently, it is easy to cause memory jitter, so we should try to reuse Bitmap memory

At the beginning of the Android 3.0 (API level 11), system introduced BitmapFactory. Options. InBitmap field. If this option is set, the decoding method using Options objects attempts to reuse inBitmap when generating the target Bitmap, which means that inBitmap memory is reused, improving performance, while removing memory allocation and unallocation. However, inBitmap can be used with some limitations. Before Android 4.4 (API level 19), only bitmaps of the same size can be reused. After Android 4.4, the size of inBitmap can be larger than the target Bitmap

4.1.4 Big picture local loading policy

There is also a case for image loading where a single image is very large and does not allow compression. For example, display: world map, Qingming River map, weibo long map and so on do not compress, according to the original figure size loading, then the screen is certainly not big enough, and taking into account the memory, can not be a one-time whole map loading into memory so this kind of optimization idea is generally local loading, through BitmapRegionDecoder to achieve

// Set the center area to display the image
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);
Copy the code

4.1.5 summary

These memory optimization strategies for Bitmaps are relatively simple, and we probably don’t use them much in development because they are already encapsulated in image frameworks like Glide, So in general, we do not need to do these special operations when loading pictures, Glide has done what optimization for loading pictures, interested students can refer to: [with questions] Glide has done what optimization?

4.2 Picture bottom strategy

In view of the foractivity,fragmentLeak caused the picture leak we can inonDetachedFromWindowThe timing is monitored and the specific process is as follows:



In this way, the cause can be conveniently solvedActivityThe resulting picture leakage problem

4.3 Online big picture monitoring scheme

When operating the online configuration is not reasonable size of the image, if we discover in time, may cause memory problems If the size of the image itself is not reasonable, on this basis we talk about the picture optimization also does not make sense, so this is also a larger image monitoring more common requirements Here are several large image monitoring plan:

4.3.1 ArtHookplan

The scheme is implemented by epic library written by Weishu, through hook of ART virtual machine, setImageBitmap of Hook ImageView and other methods to analyze and compare the bitmap width and height of the method parameters and the width and height of ImageView instance. You can also get the actual size of the Bitmap. If the image is larger than the ImageView width, or the image size exceeds the threshold, you can report the information

The advantages of this scheme lie in: 1. The invasion is very low, the initial configuration can hook the global target View control 2. The code call stack is available for quick location by developers

The disadvantages are: compatibility problems, the use of HOOK system API, can not be used online

4.3.2 BaseActivityplan

Most application projects in the process of business development will precipitation encapsulation of their BaseActivity, through the BaseActivity onDestroy dynamic detection of each View control, so as to know the picture loading situation


class BaseActivity : Activity() {fun onDestory(a){
        if(isOpenCheckBitmap){
            checkBitmapFromView()
        }
    }
    
    fun checkBitmapFromView(a){
        //1. Loop through the View controls in your activity
        // Get the Bitmap loaded by View control
        //3. Compare Bitmap width and height with View width and height}}Copy the code

The advantages of this scheme are: 1. Strong compatibility without any reflection; 2

Disadvantages are: 1. It is too intrusive and needs to modify BaseActivity; 2.BaseActivity

4.3.3 ASMplan

The scheme inserts inserts in the compilation process and inserts Bitmap size detection logic by matching key methods such as setImageBitmap and setBackground. The advantages of this scheme are as follows: 1. Piling at compile time is non-invasive to the development process

Disadvantages are as follows: 1. The insertion of piles may increase compilation time; 2.ASM code is expensive to maintain and not so convenient to use

4.3.4 registerActivityLifecycleCallbackplan

Through registerActivityLifecycleCallback to monitor the Activity life cycle, when the onStop Bitmap size detection logic

    private fun registerActivityLifecycleCallback(application: Application) {
        application.registerActivityLifecycleCallbacks(object :
            Application.ActivityLifecycleCallbacks {
            override fun onActivityStopped(activity: Activity) {
                checkBitmapIsTooBig(childViews)
            }

        })
    }
Copy the code

This scheme is non-invasive to the original code, and it is relatively simple to use and has no compatibility problems. Therefore, it should be a better scheme

5. How to doOOMOnline surveillance?

As we described above, LeakCanary can be used offline to detect memory leaks, but LeakCanary can only be used offline and has the following problems 1. Offline scenes can run to the scene is limited, it is difficult to exhaust all user scenes. 2. During the detection process, GC needs to be triggered actively, and the app freezes due to memory mirroring Dump, resulting in bad experience during the test. 3. 4. Hprof files are too large, and it will cost a lot of resources to upload them as a whole

Below we introduce kuaishou open source online OOM monitoring framework KOOM

5.1 onlineOOMMonitoring frameworkKOOMintroduce

We have described above why LeakCanary cannot be used for online monitoring, so the following issues need to be addressed in order to enable online monitoring

  • monitoring
    • Take the initiative to triggerGC, will cause a deadlock
  • collect
    • Dump hprof, can causeappfreeze
    • HprofThe file is too large
  • parsing
    • The parsing takes too long
    • Parsing itself hasOOMrisk

Its core process consists of three parts:

1. MonitorOOMWhen a problem occurs, memory image collection is triggered for further analysis

2. Collect the memory image, which is technically called heap dump, and copy the memory data to a filedump hprof

3. Analyze image files, conduct reachability analysis on leakage, super-large objects and other objects that we are concerned about, and resolve their resultsGC rootTo solve the problem

5.2 KOOMTo solveGCcaton

LeakCanary detects whether an object is recovered by multiple GC, causing performance loss. Koom triggers image collection by monitoring the memory threshold without performance loss. The specific policies are as follows:

  • JavaThe number of heap memory/threads/file descriptors exceeds the threshold
  • JavaThe collection is triggered when the heap rise rate exceeds the threshold
  • happenOOMIf policies 1 and 2 do not match, the collection is triggered
  • Leak determination is delayed until parsing

We do not need to determine whether an object is leaking at run timeActivityFor example, we don’t need to determine if it leaks at run time,ActivityThere’s a member changemDestroyedIn theonDestoryWill be set to zerotrue, as long as reachable and is found during parsingmDestroyedfortruetheActivity, can be determined as leakage

This is resolved by delaying leak determination until parsingGCCaton’s question

5.3 KOOMTo solveDump hproffreezeapp

Dump hprofThat is, the vm needs to be paused to ensure that the reference relationship does not change during the process of copying the memory data to disk. The pause time is usually more than 10 seconds, which is unacceptable to usersLeakCanaryOne of the most important reasons for not officially recommending online use.

usingCopy-on-writeMechanism,forkThe child processdumpMemory mirroring is the perfect solution to this problem,forkUpon success, the parent process immediately resumes the VM and the child process resumesdumpMemory mirroring is not affected by changes in the parent process data.

The process is as follows:



KOOMRandom collection of online memory images of real users, commondumpandforkThe child processdumpThe blocking time is as follows:



It can be seen that the basic acquisition of memory image without perception can be achieved

5.4 KOOMTo solvehprofThe file is too large

HprofFiles are usually large and analyzedOOMIs 500M abovehprofDocuments are not unusual, the size of the documents, anddumpThe success rate,dumpSpeed and upload success rate are negatively correlated, and large files waste a lot of disk space and traffic.

So there’s a need forhprofCrop and keep only the analysisOOMIn addition, clipping also has the benefit of data desensitization, only upload in-memory class and object organization structure, not upload real business data (such as strings,byteArray and other contents containing specific data), to protect user privacy.

tailoringhprofThe document refers to thehprofThe understanding of file format will not be described here

Here is a flow chart of the clipping process:

5.5 KOOMTo solvehprofParsing time andOOM

Analyzing the hprof file, analyzing the reachabability of key objects and obtaining the reference chain is the core step to solve OOM. The previous monitoring and dump are paving the way for the analysis. There are two kinds of parsing, one is to upload the hprof file to be parsed by the server, and the other is to upload the report after being parsed by the client (usually only a few KB). KOOM opted for on-side parsing, which has two advantages:

  • 1. Save user traffic
  • 2. Use the user’s idle computing power to reduceserverStress, which also fits with the idea of distributed computing.

This can break down the parsing process into the following two problems

  • Which objects need to be analyzed, all the analysis performance is too expensive, difficult to complete on the end, and the problem is not focused and not conducive to solving.
  • Performance optimization as adebugComponents, which have very high performance requirements for parsing without impacting the user experience.

5.5.1 Determination of key Objects

KOOM only analyzes the key object, which can be classified into two categories. One is the object that has been leaked and holds a large number of resources according to the rules. The other is the object whose shallow/retained size exceeds the threshold. For strongly reachable activity objects, determine that they are exposed when mDestroyed is true (onDestroy). Similarly, for fragments, when mCalled is true and mFragmentManager is null, the fragment is considered to have been leaked.

Bitmap/window/array/sufacetexture judged to be the second check the number of Bitmap/texture, high width, number of Windows, the length of the array, and so on is more than the threshold, coupled with the hprof related business information, such as screen size, View size, etc.

5.5.2 Performance Optimization

KOOMinLeakCanaryParsing enginesharkBased on some optimizations, will parse time insharkOptimized on the basis of more than 2 times, memory peak control within 100M. Summarize the parsing process with a diagram:



The detailed process is not described here, the details can be seen:KOOM resolution performance optimization

5.6 KOOMuse

KOOMCurrently open source, open source address:Github.com/KwaiAppTeam…

Refer to the access guide. If the memory exceeds the threshold or occursOOM, the memory snapshot collection is triggered. YeshprofThe file is clipped and analyzed for a report

KOOMThe report isjsonFormat, and size inKBLevel, the style is as follows:



This probably includes the following information

1. Some class information that may be leaked

2. Causes of leakagegcRoot, the number of leaked instances, etc

3. Reference chain of leakage object to facilitate problem location

It can be seen that the amount of data uploaded by KOOM is not very large, but it is relatively accurate, which is very convenient for us to analyze online data

5.7 summary

This chapter mainly introduces the open source framework of online monitoring OOM KOOM. In fact, the framework of online monitoring OOM has been developed by various manufacturers, such as Probe of Meituan and Liko of Byte. However, most of them are not officially open source, but some articles introduce the principle, and interested students can also understand it

conclusion

As for the general direction of optimization, we should give priority to the areas with quick results, mainly including the following parts: 1. Memory leakage 2. Memory jitter 3.Bitmap monitoring 4

We also introduce various utilities for memory optimization 1. You can use Profile,MAT to locate memory jitter and memory leaks during development 2. Offline development, regression, Monkey, and pressure test can automatically integrate LeakCanary to detect memory leaks. 3. Picture loading is the focus of memory optimization. We can combine picture bottom-of-the-pocket strategy and online large picture monitoring to optimize picture memory problem 4. When the memory exceeds the threshold, the system automatically dumps the memory snapshot, and analyzes OOM problems accurately by uploading analysis results.

Memory optimization is a complex process. In the process of memory optimization, we need to combine a variety of tools, online and offline, and cooperate systematically to locate and solve problems

The resources

Probe into the Android memory optimization method (purgatory level – under) more elegant detection of Android applications in the large picture Kuaishou client stability system construction Douyin Android performance optimization series: Java memory optimization