1. Common memory problems

Common memory problems fall into three categories:

1. Memory jitter

A large number of objects are created or reclaimed in a short period of time

Harm: GC is frequent, STW occurs during GC, resulting in stoning

2. Memory leaks

A program that is unable to free up allocated memory

Damage: A memory leak doesn’t seem to have much of an impact, but the result of a memory leak piling up is that it runs out of memory and causes a crash. Even if there is no memory overflow, a large amount of memory will cause GC, resulting in stuttering

3. Memory overflow

A condition in which a program cannot run because it needs more memory than the maximum available memory

Hazard: The program cannot run

Two, common memory analysis tools

1, the Memory Profiler

The memory analysis tool provided by AndroidStudio is often used to intuitively determine whether there is memory jitter and memory leaks

As shown in the figure, you can intuitively see the fluctuation of memory to determine whether there is a memory problem

2, Mat

Mat can be used for in-depth analysis of memory jitter and memory leaks

The usage method is as follows:

1. First, use the heap dump function of Memory Profiler to record the Memory status in a period of time and store it as hprof file.

2. Convert hprof files saved from Memory Profiler using hprofe-conv. Mat cannot parse files saved directly from Memory Profiler, but Android does provide a conversion tool called Hprof-conv. Hprof-conv is in the platform-tools directory of the Android SDK. Use the following command

Hprof-conv Indicates the converted hprof file

3. Use Mat to parse the transformed Hprof file

Click the button below to open the hprof file

4. Basic functions of Mat

View a class reference correlation

  • With Incoming References: Indicates which external references are used for this class
  • With outgoing References: Indicates which references to external objects this class holds

Check the GC Root

In dominator_tree, look for Activity->Path To GC Roots-> With all references

In this case, the results are as follows:

As a result, outside of the system class reference, the Activity is referenced by the PluginManager singleton, causing a leak and the Activity cannot be released

3, LeakCanary

LeakCanary is an automated memory leak analysis tool

Method of use

1. Increase dependency

The compile 'com. Squareup. Leakcanary: leakcanary - android: 1.6.1'Copy the code

2. Enable detection in Application

public class MyApplication extends Application {


    @Override
    public void onCreate(a) {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }
        LeakCanary.install(this); }}Copy the code

3. When operating the APP, a notification will appear in the notification bar of the phone if there is a leak. After opening it, you can see the following interface of memory leak

LeakCanary principle

RefWatcher. Watch () creates a KeyedWeakReference to the object to be monitored.

2. The background thread then checks to see if the reference is cleared, and if not, calls GC.

3. If the reference is still not cleared, dump the heap memory to an.hprof file in the corresponding file system of the APP.

HeapAnalyzerService in another process has a HeapAnalyzer that uses HAHA to parse this file.

5. Thanks to the unique Reference key, HeapAnalyzer finds KeyedWeakReference to locate memory leaks.

6. HeapAnalyzer calculates the shortest strong reference path to GC roots and determines if it is a leak. If so, set up the chain of references that led to the leak.

7. The reference chain is passed to the DisplayLeakService in the APP process and displayed as a notification.

Weak reference and reference queue are used to determine whether an object can be reclaimed. When an object can be reclaimed, it will be added to the associated reference queue. To determine if the object is leaking

The following code is written with reference to the LeakCanary source code, which basically shows the LeakCanary detection principle

class RefWatcher {

    private val referenceQueue = ReferenceQueue<Any>()

    private val retainedKeys = HashSet<String>()

    private val gcTrigger = GcTrigger.Default

    private val executor = Executors.newSingleThreadExecutor()

    /** * When the Activity destroys, perform a check to see if it leaks */
    fun watch(obj : Any) {
        val key = UUID.randomUUID().toString()
        retainedKeys.add(key)
        val reference = KeyedWeakReference(key, obj, referenceQueue)
        executor.execute {
            // Clears objects added to the queue
            removeReachableReferences()
            // First judgment
            if(gone(reference)) {
                // No leaks
            } else {
                // Run a gc, which suspends the thread for 100ms
                gcTrigger.runGc()
                // Clear the reference added to the ReferenceQueue again
                removeReachableReferences()
                // Check again
                if(gone(reference)) {
                    // No leaks
                } else{
                    // Determine a leak}}}}/** * When an object can be collected by GC, it is added to the referenceQueue ** When added to the referenceQueue reference, the object is judged not to leak and removed from the retainsKey */
    private fun removeReachableReferences(a) {
        var ref  = referenceQueue.poll() as KeyedWeakReference
        while(ref ! =null) {
            retainedKeys.remove(ref.key)
            ref = referenceQueue.poll() as KeyedWeakReference
        }
    }

    /** * retainedKeys do not contain, then judged to be recyclable, will not leak */
    private fun gone(reference: KeyedWeakReference) : Boolean {
        return! retainedKeys.contains(reference.key) } }Copy the code

4. ResourceCanary of Matrix

ResourceCanary is a module for detecting memory leaks in Tencent’s APM. The implementation of ResourceCanary is very similar to leakCanary, with two differences:

1. LeakCanary Is suspended for 100 seconds after gc is called, because the vm does not necessarily perform GC immediately after gc is called, but there is a possibility of miscalculation and there may be no GC. ResourceCanary improves the practice by setting a weak reference to a common recyclable object before gc, using this reference as a sentry. When GC occurs, the object is reclaimed, otherwise, the object is still alive. This object is used to determine whether the virtual machine has actually performed GC.

2. LeakCanary analyzes the complete hprof file, while ResourceCanary dumps the complete hprof file and then culls it. One more trim than leakCanary, reducing the hprof file size

However, neither leakCanary nor ResourceCanary is suitable for direct online use, as the full hprof file is too large, possibly hundreds of megabytes, to be suitable for online execution.

Bytedance’s Tailor

Tailor is a memory snapshot tailoring and compression tool. Hook technology is used to Tailor files during WRITE on the C layer, avoiding the need to save the entire hprof file and dump the entire hprof file

The concrete implementation of the repository to view: https://github.com/bytedance/tailor

6, StrictMode

StriceMode is an official runtime monitoring tool from Google. StriceMode contains virtual machine policies and thread policies. The virtual machine policies can detect Activity leaks, Sql object leaks, and whether the number of instances of a class exceeds the limit

        StrictMode.setVmPolicy(
                new StrictMode.VmPolicy.Builder()
                        // Detect Activity leaks
                        .detectActivityLeaks()
                        // Detect database object leaks
                        .detectLeakedSqlLiteObjects()
                        // Check the number of instances of a class
                        .setClassInstanceLimit(PluginManager.class, 1)
                        // Test results are printed in log, or exit application if leakage is optional
                        .penaltyLog()
                        .build());
Copy the code

3. Memory jitter and memory leakage

1. Memory jitter

How to find: Using memory profiler observations, memory is jagged, high and low

Common cause: Programs frequently create objects. Or it could be a memory leak that causes out of memory and frequent gc reapplications

Harm: Frequent GC, resulting in gridlock

The solution: Memory profilers can be used to observe Memory jitter and check the parts of Memory that are allocated more and have more instances to find stack faults

2. Memory leaks

Definition: There are useless objects in memory that cannot be reclaimed

Damage: The available memory is gradually reduced, or memory overflow may occur

Solution: Use the memory profiler to check whether the available memory is decreasing. If so, there may be a memory leak.

Download the hprof file using the heap dump function in as. Convert the format using the tools in Platform Tools and open the analysis using MAT.

Usually find the activity, check the number of existing objects, if more than one object is abnormal, use mat to check its GC roots reference, find the cause of processing

Common causes of memory leaks

1. Collection classes

When the collection class adds elements, it does not clean up the collection in time after use, causing the elements in the collection to be unable to be freed

List<Object> objectList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Object o = new Object(); objectList.add(o); O = null; // objectList exists. }Copy the code

2. Static variables

static Context mContext;
Copy the code

If the Activity is referenced by a static variable, it will not be released, so be careful

3, singleton

Singletons are particularly easy to ignore and need to form a habit of avoiding leakage

public class SingleInstanceClass { private static SingleInstanceClass instance; private Context mContext; private SingleInstanceClass(Context context) { mContext = context; } public static SingleInstanceClass getInstance(Context context) { if (instance == null) { instance = new SingleInstanceClass(context); } return instance; }}Copy the code

In this case, if the context is passed as an Activity, it will be leaked, so you should pass the Application context

4. Non-static inner classes/anonymous classes

Non-static inner classes/anonymous classes hold references to outer classes, while static inner classes do not

This often happens with handlers that are referred to by a Message, and messages can fire with a delay of, say, 6 seconds. Within 6 seconds, the Activity has been destroyed. The Handler cannot be released, and if the Handler is a non-static inner class/anonymous class, it will reference the Activity, which will cause leakage

5, summary

This article introduces three common memory problems, six tools for memory analysis and detection, common ways to deal with memory jitter, memory leaks, and a variety of common causes of memory leaks caused by improper encoding in Android. In actual production, the memory problem is very hidden and difficult to find, it is best to cooperate with the online and offline processing, the offline use of excellent tools such as leakCanary detection, online through the key module memory, OOM rate, GC times and so on return, automatic analysis and monitoring for some users memory snapshot return operation. To continuously optimize memory at lower cost.