After reading this article, we can solve 90% of the memory exception problem in APP.

Why do we optimize memory

The. java file we write in Android will eventually be compiled into. class file. After class is loaded by class loader, a meta-information object describing class structure will be formed in JVM. Through this meta-information object, we can know the class structure information (constructor, attribute, method) and so on.

JVM loads data describing classes from class files into memory. Java has a good memory management mechanism, garbage collection mechanism GC.

Why does Java provide us with garbage collection mechanism? Programs sometimes lead to memory leaks, memory overflow OOM, and even Crash. Next, we will optimize these memory problems in actual development.

JAVA Virtual Machine

Let's first get a general idea of the data areas that run in the Java Virtual Machine.

Thread exclusive zone

Program counter

  • An indicator for executing code that identifies the address of the next line of execution
  • Each thread has one
  • Zones without OOM

VM Stack

  • The stack we usually talk about is this area.
  • OutOfMemeory, stackoverflow exception are defined in the java virtual machine specification

Native Method Stack

  • OutOfMemory, stackoverflow exception are defined in the java virtual machine specification

Be careful

  • In hotspot VM, virtual machine stacks and local methods are stacked into a stack area

Thread Sharing Zone

Method area

  • ClassLoader loads class information
  • Constants, static variables
  • Compiled code
  • OOM will appear
  • Runtime Constant Pool
    • public static final
    • Symbol reference class, interface full name, method name

java heap (where optimization is needed this time)

  • The Largest Memory GC Main Battlefield of Virtual Function Management
  • OOM will appear
  • Object instance
  • Content of data

How JAVA GC Determines Memory Recycling

With the running of the program, more and more instance objects and variables occupy the memory. If they are not recovered in time, it will reduce the efficiency of the program and even cause system abnormalities.

At present, virtual machines mostly use reachability analysis algorithm. Why not use reference counting algorithm? Now let's talk about the reference counting method. If we count the reference counting of all objects, then compare the accessibility analysis algorithm how to solve the deficiency of the reference counting algorithm. Let's look at these two algorithms:

Reference Counting Algorithms

Each object has a reference counter. When the object is referenced once, the counter adds one. When the object is referenced once, the counter decreases one. When the counter is zero, it means garbage and can be recycled by GC.

Let's take a look at it in a piece of code.

public class GCTest {
    private Object instace = null;

    public static void onGCtest() {
        //step 1
        GCTest gcTest1 = new GCTest();
        //step 2
        GCTest gcTest2 = new GCTest();
        //step 3
        gcTest1.instace = gcTest2;
        //step 4
        gcTest2.instace = gcTest1;
        //step 5
        gcTest1 = null;
        //step 6
        gcTest2 = null;

    }

    public static void main(String[] arg) {
        onGCtest();
    }
}

Analysis code

// step 1 gcTest1 Reference + 1 = 1
 // step 2 gcTest2 Reference + 1 = 1
 // step 3 gcTest1 Reference + 1 = 2
 // step 4 gcTest2 Reference + 1 = 2
 // step 5 gcTest1 Reference - 1 = 1
 // step 6 gcTest2 Reference - 1 = 1

Obviously both objects can't be used for null now, but GC can't reclaim them because their reference counts are not zero. It can't meet the condition of being recycled, even though calling System.gc() can't be recycled, which results in memory leak. Of course, nowadays virtual machines basically don't use this approach.

Reachability analysis algorithm

Starting with GC Roots as the starting point, the frontal edges of the connected graph are all live objects, and the objects that cannot be reached by GC Roots become garbage collection objects, which may be recycled by GC at any time.

Can be used as an object for GC Roots

  • References for running virtual machine stacks
  • Static attribute constants
  • Objects referenced by JNI

GC needs two scans to reclaim the object, so we can use finalize to save the lost reference.

 @Override
    protected void finalize() throws Throwable {
        super.finalize();
        instace = this;
    }

Here, I believe you can understand the difference between the two algorithms, right? In the case of circular references between objects, reference counting algorithm can not recover these two objects, and accessibility is searched from GC Roots, so it can recover correctly.

Recycling status for different reference types

Strong citation

Object strongReference = new Object()

If an object has strong references, the garbage collector will never reclaim it. When the memory space is insufficient, the Java Virtual Machine would rather throw OOM errors to make the program exception Crash, rather than reclaim the object with strong references at will to solve the problem of insufficient memory. If the object with strong references is no longer used, it needs to be weakened. To enable GC to be recycled, it is necessary to:

strongReference = null; //Wait for GC to recycle

In another case, if:

public void onStrongReference(){
    Object strongReference = new Object()
}

There is a strong reference inside onStrongReference(), which is stored in the java stack, while the real reference content (Object) is stored in the java heap. When the method runs, it exits the method stack, and the reference number of the reference object is 0, which is reclaimed.

But if the mStrongReference reference is global, you need to assign null when you don't use the object, because strong references are not reclaimed by GC.

SoftReference

If an object has only soft references, the memory space is sufficient, and the garbage collector will not reclaim it; if the memory space is insufficient, the memory of these objects will be reclaimed. As long as the garbage collector does not reclaim it, the object can be used by the program. Soft references can be used to implement memory-sensitive caching.

Soft references can be used in conjunction with a Reference Queue, and if the object referenced by a soft reference is recycled by the garbage collector, the java virtual machine adds this soft reference to the reference queue associated with it.

Note: Soft reference objects are reclaimed only when the JVM memory is insufficient. We call the System.gc() method only for notification. When the JVM scans the reclaimed objects is determined by the JVM's own state. Even if the str object is scanned, it will not be reclaimed. Only when there is insufficient memory will it be reclaimed.

WeakReference

The difference between weak references and soft references is that objects with only weak references have a shorter life cycle. When a garbage collector thread scans the memory area under its jurisdiction, once it finds an object with only weak references, its memory will be reclaimed regardless of whether the current memory space is sufficient or not. However, since the garbage collector is a low priority thread, it is not necessarily quick to find objects with only weak references.

Weak references can be used in conjunction with a reference queue, and if the object referenced by a weak reference is garbage collected, the Java Virtual Machine adds this weak reference to the reference queue associated with it.

It can be seen that the life cycle of the weakReference object is basically determined by the GC. Once the GC thread finds a weak reference, it marks it down, and it is reclaimed directly after the second scan.

Note that the reference Queuee here is a loaded recycled object.

Phantom Reference

    @Test
    public void onPhantomReference()throws InterruptedException{
        String str = new String("123456");
        ReferenceQueue queue = new ReferenceQueue();
        // Creating virtual references requires that they must be associated with a reference queue
        PhantomReference pr = new PhantomReference(str, queue);
        System.out.println("PhantomReference:" + pr.get());
        System.out.printf("ReferenceQueue:" + queue.poll());
    }

As the name implies, virtual reference refers to nothing in form, which is different from other kinds of references. Virtual reference does not determine the life cycle of an object. If an object only holds virtual references, it can be reclaimed by the garbage collector at any time, just as it does not have any references.

Virtual references are mainly used to track objects being recycled by garbage collectors. One difference between virtual references and soft and weak references is that virtual references must be used in conjunction with reference queues. When the garbage collector is ready to reclaim an object, if it finds that it has a virtual reference, it adds the virtual reference to the reference queue associated with it before reclaiming the object's memory.

summary

reference type Call mode GC Is there a memory leak?
Strong citation Direct call No Recycling yes
Soft citation .get() Recycling Depending on Memory Conditions no
Weak citation .get() recovery Impossible
Virtual reference null It can be recycled at any time, as if there were no references. no

Common tools for analyzing memory

There are many tools, master the principle and method, and choose and use the tools at will.

  • top/procrank
  • meinfo
  • Procstats
  • DDMS
  • MAT
  • Finder - Activity
  • LeakCanary
  • LeakInspector

Memory leak

Cause: An object with a long life cycle holds a reference to a short life cycle object, which in general is the object to be reclaimed, because the reference problem is not reclaimed, and eventually OOM will be generated.

Now let's use Profile to check if there is a memory leak in the project.

How to use profile to see if there is a memory leak in a project

1. The project runs in profile in AS

2. Select a section of MEMORY to analyze in the MMORY interface, right-click export

Allocations: Dynamic allocation of number of objects
Deallocation: Number of Objects Unallocated
Total count: Total number of objects
Shalow Size: The size of memory occupied by the object itself
Retained Size: The size of memory that GC recycles can take away

3. Transform profile file format

*   take export Derived dprof File conversion to Mat Of dprof file

*   cd /d Enter Android sdk/platform-tools/hprof-conv.exe

    ```
    //Conversion command hprof-conv-z SRC des
    D:\Android\AndroidDeveloper-sdk\android-sdk-windows\platform-tools>hprof-conv -z D:\temp_\temp_6.hprof D:\temp_\memory6.hprod
    ```

4. Download Mat Tool

5. Open Memory Analyzer.exe and click Open Heap Dupm in the File menu in the upper left corner

6. View GC Roots strong references in memory leaks

Here we know that an ilsLoginListener refers to LoginView, so let's see how the code finally solves it.

In the code, we found the LoginView class and found that it was a memory leak caused by callbacks in a singleton. Here's how to solve the problem. See the seventh dot.

7. Two Solutions to Memory Leakage in Singletons

  1. Set the reference to null
        /**
             * Destruction monitoring
             */
            public void unRemoveRegisterListener(){
                mMessageController.unBindListener();
            }
            public void unBindListener(){
                if (listener != null){
                    listener = null;
                }
            }
  1. Use weak references

    ```
    //Placing listeners in weak references
    WeakReference<IBinderServiceListener> listenerWeakReference = new WeakReference<>(listener);
    
    //Remove callbacks from weak references
    listenerWeakReference.get();
    ```
    
8. Memory leaks caused by callbacks in singletons can be perfectly solved by the seventh smallest point.

Classic Memory Leakage Cases and Solutions in Android

Single case

**Example :**

```
public class AppManager {

    private static AppManager sInstance;
    private CallBack mCallBack;
    private Context mContext;

    private AppManager(Context context) {
        this.mContext = context;
    }

    public static AppManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppManager(context);
        }
        return sInstance;
    }

    public void addCallBack(CallBack call){
        mCallBack = call;
    }
}
```
  1. Through the single column above, if context passes in Activity, Service's this, memory leaks will result.

Take Activity as an example. When Activity calls getInstance to pass in this, sInstance holds a reference to Activity. When Activity needs to be recycled when it needs to be closed, it is found that sInstance also holds a reference to useless Activity, leading to the inability of Activity to be recycled by GC, resulting in memory leaks.

  1. There seems to be nothing wrong with addCallBack(CallBack call). But when this call is made, look at Le.

    ```
    //Implementing callbacks for singletons in Activity
    AppManager.getInstance(getAppcationContext()).addCallBack(new CallBack(){
        @Override
        public void onStart(){
    
        }
    });
    ```
    

Here, the new CallBack() anonymous inner class holds external references by default, which makes CallBack unreleasable. So how to solve this problem? See the following solution

Solution:

  1. GetInstance (Context context) contexts are passed into the Application-level Content ext, or the WeakReference is used for references that really need to be passed into Activity.

  2. Anonymous inner classes suggest that you write a single file or

    ```
    public void addCallBack(CallBack call){
            WeakReference<CallBack> mCallBack= new WeakReference<CallBack>(call);
        }
    ```
    
  3. Handler

    Examples:

    //Implementing Handler in Activity
    class MyHandler extends Handler{
        private Activity m;
        public MyHandler(Activity activity){
            m=activity;
        }
    
    //    class.....
    }
    

Here MyHandler holds a reference to activity, which causes the GC to not reclaim and cause memory leaks when the activity is destroyed.

Solution:

```
1.Using static inner classes + Weak citation
2.stay Activity onDestoty() Intermediate treatment  removeCallbacksAndMessages() 
    @Override
    protected void onDestroy() {
        super.onDestroy();
    if(null != handler){
          handler.removeCallbacksAndMessages(null);
          handler = null;
    }
 }
```
  1. Static variables

    Examples:

    public class MainActivity extends AppCompatActivity {
    
        private static Police sPolice;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sPolice != null) {
                sPolice = new Police(this);
            }
        }
    }
    
    class Police {
        public Police(Activity activity) {
        }
    }
    

Here policy holds a reference to activity, which causes the activity not to be released, leading to memory leaks.

Solution:

```
// 1. sPolice in onDestory () sPolice = null;
// 2. Strong to weak references will be referenced in the Police constructor;
```
  1. Non-static internal classes

Refer to the second point, Handler's handling

  1. Anonymous Inner Class

Examples:

public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(){
                 @Override
                 public void run() {
                    super.run();
                            }
                        };
        }
    }

Many beginners will create new threads and asynchronous tasks like the above, but it is unfriendly. Thread and AsyncTask are anonymous internal class objects, which implicitly hold external Activity references by default, leading to Activity memory leaks.

Solution:

// Static Internal Class + Weak Reference
 // Write a single file + onDestory = null;
  1. Unregistered or callback

Examples:

public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            registerReceiver(mReceiver, new IntentFilter());
        }

        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // TODO ------
            }
        };
    }

Memory leaks can also occur if the mode is not cancelled in time when registering observations. For example, using Retrofit + RxJava to register an observer callback for a network request also holds an external reference as an anonymous internal class, so you need to remember to cancel the registration when it is not used or destroyed.

Solution:

//Implementation of onDestory () Anti-Registered Broadcast in Activity is Released
        @Override
        protected void onDestroy() {
            super.onDestroy();
            this.unregisterReceiver(mReceiver);
        }
  1. Timing tasks

Examples:

public class MainActivity extends AppCompatActivity {

        /**Analog Counting*/
        private int mCount = 1;
        private Timer mTimer;
        private TimerTask mTimerTask;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
            mTimer.schedule(mTimerTask, 1000, 1000);
        }

        private void init() {
            mTimer = new Timer();
            mTimerTask = new TimerTask() {
                @Override
                public void run() {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            addCount();
                        }
                    });
                }
            };
        }

        private void addCount() {
          mCount += 1;
        }
    }

When our Activity is destroyed, it is possible that Timer is still waiting to execute TimerTask, which holds references to Activity that cannot be reclaimed by GC, so when we destroy Activity, cancel Timer and TimerTask immediately to avoid memory leaks.

Solution:

//When Activity closes, stop all ongoing scheduled tasks to avoid memory leaks.
       private void stopTimer() {
           if (mTimer != null) {
               mTimer.cancel();
               mTimer = null;
           }
           if (mTimerTask != null) {
               mTimerTask.cancel();
               mTimerTask = null;
           }
       }

       @Override
       protected void onDestroy() {
           super.onDestroy();
           stopTimer();
       }
  1. Resources not closed

Examples:

When resources such as ArrayList,HashMap,IO,File,SqLite,Cursor are exhausted, it is important to remember that clear remove closes a series of operations on resources.

Solution:

    Destroy immediately after use
  1. Attribute animation

    Examples:

Animation is also a time-consuming task, such as launching the Object Animator in Activity, but when destroyed, we did not call the cancle method. Although we can not see the animation, the animation will continue to play. The animation refers to the control in which the animation refers to the Activity, and the control in which the animation refers to the Activity. Y, which causes Activity not to be released properly. So cancel the attribute animation when the activity is destroyed to avoid memory leak.

Solution:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //When closing Activity, remember to close the animation operation
        mAnimator.cancel();
    }
  1. Android source code or third-party SDK

Examples:

What if we meet Android source code or third-party SDK that holds our current Activity or other classes in development and debugging?

Solution:

Currently, a class or member is found by reflection in Java for manual = null operations.

memory thrashing

What is memory jitter

Frequent allocation and recovery of memory (when allocation speed is faster than recovery speed) eventually results in OOM.

Perhaps the following video can better explain what memory jitter is.

You can see that when I click on Button memory, it is created and recycled frequently (watch the garbage can).

So let's find out which part of the code has a problem. Look at the video below.


mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imPrettySureSortingIsFree();
            }
        });

/**
     *&emsp;Print two-dimensional arrays after sorting, one line at a time
     */
    public void imPrettySureSortingIsFree() {
        int dimension = 300;
        int[][] lotsOfInts = new int[dimension][dimension];
        Random randomGenerator = new Random();
        for (int i = 0; i < lotsOfInts.length; i++) {
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                lotsOfInts[i][j] = randomGenerator.nextInt();
            }
        }

        for (int i = 0; i < lotsOfInts.length; i++) {
            String rowAsStr = "";
            //sort
            int[] sorted = getSorted(lotsOfInts[i]);
            //Stitching Printing
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                rowAsStr += sorted[j];
                if (j < (lotsOfInts[i].length - 1)) {
                    rowAsStr += ", ";
                }
            }
            Log.i("ricky", "Row " + i + ": " + rowAsStr);
        }
    }

Finally, we followed by rowAsStr += sorted[j] in the imPrettySureSortingIsFree() function of onClick; the memory jitter caused by string splicing, because each splicing of a String will apply for a new heap memory, so how to solve the problem of frequent memory creation?

In fact, there are two better API s in Java that are very friendly to String, and I believe someone should have guessed that. That's right. Replacing String Buffer or String Builder with String Buffer is a perfect solution to the memory jitter caused by string splicing.

Revised

        /**
         *&emsp;Print two-dimensional arrays, one line
         */
        public void imPrettySureSortingIsFree() {
            int dimension = 300;
            int[][] lotsOfInts = new int[dimension][dimension];
            Random randomGenerator = new Random();
            for(int i = 0; i < lotsOfInts.length; i++) {
                for (int j = 0; j < lotsOfInts[i].length; j++) {
                    lotsOfInts[i][j] = randomGenerator.nextInt();
                }
            }

            // Using StringBuilder to complete the output, we just need to create a string without wasting too much memory.
            StringBuilder sb = new StringBuilder();
            String rowAsStr = "";
            for(int i = 0; i < lotsOfInts.length; i++) {
                // Clear up the previous line
                sb.delete(0, rowAsStr.length());
                //sort
                int[] sorted = getSorted(lotsOfInts[i]);
                //Stitching Printing
                for (int j = 0; j < lotsOfInts[i].length; j++) {
                    sb.append(sorted[j]);
                    if(j < (lotsOfInts[i].length - 1)){
                        sb.append(", ");
                    }
                }
                rowAsStr = sb.toString();
                Log.i("jason", "Row " + i + ": " + rowAsStr);
            }
        }

summary

As long as you develop this habit, you can avoid at least 90% of memory abnormalities.

Data Types: ** Do not use basic data types that take up more space than requirements
2. Use foreach as much as possible, iterator as little as possible, and automatic packing as little as possible.
3. Solution Processing of Data Structure and Algorithms (Array, Link List, Stack Tree, Tree, Graph)

Sparse arrays (Key is an integer) can be used within the 1000-level data volume, and Array Map (Key is an object) saves memory even though its performance is not as good as HashMap.

4. Enumeration optimization

Disadvantages:

Each enumeration value is a singleton object, which adds extra memory consumption when used, so enumeration takes up more memory than Integer and String.

More use of Enum will increase the size of DEX files, cause more IO overhead at runtime, and make our application need more space.

Especially for large apps with more than Dex, the initialization of enumeration can easily lead to ANR

Optimized code: You can directly limit the number of parameters passed in

    public class SHAPE {
        public static final int TYPE_0=0;
        public static final int TYPE_1=1;
        public static final int TYPE_2=2;
        public static final int TYPE_3=3;

        @IntDef(flag=true,value={TYPE_0,TYPE_1,TYPE_2,TYPE_3})
        @Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})
        @Retention(RetentionPolicy.SOURCE)
        public @interface Model{

        }

        private @Model int value=TYPE_0;
        public void setShape(@Model int value){
            this.value=value;
        }
        @Model
        public int getShape(){
            return this.value;
        }
    }

5. Static, static final issues

  • static is initialized by the compiler calling the clinit method
  • static final does not require initialization. It is packaged in dex file and can be invoked directly. It does not request memory for class initialization.

Members of basic data types can be written as static final

6. String splicing should be used as little as possible+=

7. Repeated application memory problem

  • The same method is called many times, such as recursive function, new object in callback function.
  • Do not refresh the UI (request Layout) in onMeause (), onLayout (), onDraw ().

8. Avoid GC reclaiming objects for future reuse (memory design pattern object pool + LRU algorithm)

9. Activity Component Leakage

  • Non-business needs do not pass parameters to the context of activity, you can pass the context of application
  • Non-static inner classes and anonymous inner classes hold active references (static inner classes or write files separately)
  • Callbacks in singleton mode hold active references (weak references)
  • handler.postDelayed() problem
  • If the open thread needs to pass in parameters, weak fuse reception can solve the problem.
  • handler remembers to clear away removeCallbacks AndMessages (null)

10. Service time-consuming operations try to use IntentService instead of Service

Last

If you think the article is well written, give it a compliment? If you think it's worth improving, please leave me a message. We will inquire carefully and correct the shortcomings. Thank you.

I hope you can forward, share and pay attention to me, and update the technology dry goods in the future. Thank you for your support! ___________

Forwarding + Praise + Concern, First Time to Acquire the Latest Knowledge Points

Android architects have a long way to go. Let's work together.

The following wall cracks recommend reading!!!

Finally, I wish you all a happy life.~

Keywords: Java Android jvm SDK

Added by kelly001 on Wed, 07 Aug 2019 13:24:17 +0300