The purpose of optimizing memory is

In Android, the.java files we write are eventually compiled into.class files, which are loaded by the class loader, and form a meta-information object in the JVM that describes the class structure. This meta-information object lets you know the class’s structural information (constructors, properties, methods), and so on. The JVM loads data describing classes from class files into memory, and Java has a good mechanism for managing memory, the garbage collection (GC). Why Java has provided us with garbage collection mechanism, the program will sometimes lead to memory leakage, memory overflow OOM, and even lead to program Crash. Next, we will optimize these memory problems in actual development.

The JAVA virtual machine

Let’s take a look at the run-time data areas of the Java virtual machine. If you want to learn more about the Java Virtual machine, you can buy the book: Understanding the Java Virtual Crisis in Depth.

Thread exclusive area

Program counter

  • An indicator of execution code that identifies the address of the next line of execution
  • Each thread has one
  • There is no OOM section

The virtual machine stack

  • This is what we call the stack
  • The Java virtual machine specification defines OutOfMemeory, stackOverflow exception

Local method stack

  • OutOfMemory is defined in the Java virtual machine specification, stackOverflow exception

  • Pay attention to

  • HotspotVM combines the virtual machine stack and the local method stack into one stack area

Thread shared area

Methods area

  • ClassLoader loads class information
  • Constant, static variables
  • Compiled code
  • There will be a OOM
  • Runtime constant pool – public static final – Symbolic reference class, full interface name, method name

Java heap (need to be optimized this time)

  • Virtual machine management of the largest chunk of memory GC main battleground
  • There will be a OOM
  • Object instance
  • Content of data

How does the JAVA GC determine memory collection

With the running of the program, instance objects and variables occupy more and more memory. If the memory is not reclaimed in time, the program running efficiency may be reduced or even system exceptions may occur.

At present, most virtual machines use the reachability analysis algorithm. Why not use the reference counting algorithm? Let’s talk about how the reference counting method counts the reference counts of all objects, and then compare how the reachability analysis algorithm solves the shortcomings of the reference counting algorithm. Here are the two algorithms:

Reference counting algorithm:

Each object has a reference counter, which is incremented by one when the object is referenced once, and decrement by one when the object is referenced once. When the counter is 0, it is garbage and can be collected by GC.

Let’s take a look at it in practice with 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();
      }
  }
Copy the code

Analysis of the 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 = 1Copy the code

It is clear that both objects are now unusable and null, but GC cannot reclaim them because they do not have a reference count of zero. Failure to meet the criteria for being collected, despite calls to System.gc(), causes a memory leak. Of course, today’s virtual machines don’t use this approach.

Accessibility analysis algorithm

If GC Roots is used as the starting point for searching, then all the edges of objects in the whole connected graph are live objects. For objects that GC Roots cannot reach, they become garbage collection objects and may be collected by GC at any time.

Can be an object for GC Roots

  • The reference used by the virtual machine stack is running
  • Static attribute constant
  • Objects referenced by JNI

GC takes 2 scans to collect objects, so we can use Finalize to save lost references

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

At this point, I think you can see the difference between these two algorithms, right? Anyway, in the case of cyclic references between objects, the reference counting algorithm cannot recover these two objects, and the reactability starts from GC Roots, so it can recover correctly.

Reclaim status for different reference types

Strong reference

  Object strongReference = new Object()
Copy the code

If an object has a strong reference, the garbage collector will never reclaim it. When the memory space is insufficient, the Java virtual machine would rather throw an OOM error and Crash the program, rather than randomly reclaim objects with strong references to solve the memory problem. If a strongly referenced object is no longer in use and needs to be weakened so that the GC can reclaim it, you need:

strongReference = null; // Wait for GC to collectCopy the code

There is another case if:

  public void onStrongReference(){
      Object strongReference = new Object()
  }
Copy the code

There is a strong reference inside onStrongReference(), which is stored in the Java stack, and the actual reference content (Object) is stored in the Java heap. When the method is finished, it exits the stack, the number of references to the object is zero, and the object is reclaimed.

However, if the mStrongReference reference is global, you need to assign null if the object is not used, because strong references are not collected by the GC.

Soft references (SoftReference)

If an object has only soft references, there is enough memory for the garbage collector not to reclaim it; If it runs out of memory, the object’s memory is reclaimed, and 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.

A soft reference can be used in conjunction with a ReferenceQueue (ReferenceQueue), and if the object referenced by the soft reference is collected by the garbage collector, the Java virtual machine adds the soft reference to the ReferenceQueue associated with it.

Note: Soft reference objects are only reclaimed when the JVM runs out of memory, so we call system.gc () to notify the JVM that when it scans for reclaimed objects is the JVM’s own state. Even if STR is scanned, it will not be collected, only if there is insufficient memory.

A weak reference (WeakReference)

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 only have weak references may not be found quickly.

A weak reference 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 the weak reference to the reference queue associated with it.

It can be seen that the life cycle of weakReference object is basically determined by GC. Once the GC thread finds a weakReference, it will mark it and recycle it when it is scanned for the second time.

Note that the referenceQueuee is installed as the object being recycled.

Phantom reference (PhantomReference)

 @Test
    public void onPhantomReference()throws InterruptedException{
        String str = new String("123456"); ReferenceQueue queue = new ReferenceQueue(); PhantomReference pr = new PhantomReference(STR, queue); System.out.println("PhantomReference:" + pr.get());
        System.out.printf("ReferenceQueue:" + queue.poll());
    }
Copy the code

A virtual reference is, as its name implies, a virtual reference. Unlike other references, a virtual reference does not determine the life cycle of an object. If an object holds only virtual references, it can be collected by the garbage collector at any time, just as if there were no references at all.

Virtual references are mainly used to track the activity of objects being collected by the garbage collector. One difference between a virtual reference and a soft or weak reference is that a virtual reference must be used in conjunction with a ReferenceQueue. When the garbage collector is about to reclaim an object and 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.

conclusion

Common tools for analyzing memory

A lot of tools, master the principle and method, tools at will to choose and use.

top/procrank

meinfo

Procstats

DDMS

MAT

Finder – Activity

LeakCanary

LeakInspector

A memory leak

Cause: A long-life object holds a reference to a short-life object, which is generally referred to as the object to be recycled, because the reference problem is not recycled, resulting in OOM.

Let’s use profiles to check for memory leaks in projects

How do I use profiles to see if there are memory leaks in a project

1. In AS, projects run AS profiles

2. On the MEMORY screen, select the MEMORY to be analyzed and right-click Export

Allocations: Dynamically allocates number of objects

Deallocation: Number of unallocated objects

Total Count: indicates the Total number of objects

Shalow Size: memory Size occupied by the object itself

Retained Size: The Size of memory that the GC can retrieve

3. Convert the profile file format

  • Convert the dprof file exported by export to Mat’s dprof file

  • CD /d Go to Android SDK /platform-tools/hprof-conv.exe

// convert to 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.hprofCopy the code

4. Download Mat tool

5. Open MemoryAnalyzer. Exe click Open Heap Dupm in the File menu in the upper left corner

6. Look for GC Roots strong references in memory leaks

So here we see ilsLoginListener referring to LoginView, so let’s see what that does.

LoginView = LoginView = LoginView = LoginView = LoginView = LoginView = LoginView

7.2 Ways to resolve memory leaks in singletons

  • 1. Set the reference to NULL

/** * Destroy listener */ public voidunRemoveRegisterListener(){
          mMessageController.unBindListener();
      }
      public void unBindListener() {if (listener != null){
              listener = null;
          }
      }
Copy the code

  • 2. Use weak references

WeakReference<IBinderServiceListener> listenerWeakReference = new WeakReference<>(listener); / / from a weak reference to retrieve the callback listenerWeakReference. The get ();Copy the code

8. The seventh dot perfectly solves the memory leak caused by callbacks in singletons.

Common Android memory leak classic cases and solutions

1. The singleton

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);
          }
          returnsInstance; } public void addCallBack(CallBack call){mCallBack = call; }}Copy the code

1. Through the single column above, if the context passes in this for an Activity, then a memory leak will occur.

In the case of an Activity, when the Activity calls getInstance and passes this, sInstance holds a reference to the Activity, and when the Activity needs to be closed and recycled, A memory leak occurs when sInstance also holds a reference to an Activity that is not used, preventing the Activity from being collected by the GC

2. AddCallBack (CallBack call) looks fine. But when called in this way look at le.

// Implement the singleton callback appManager.getInstance (getAppcationContext()).addCallback (new) in the ActivityCallBack(){
      @Override
      public void onStart() {}});Copy the code

The new CallBack() anonymous inner class holds an external reference by default, so CallBack cannot be released.

Solutions:

1. GetInstance (Context Context) Pass in Appcation level Context, or use WeakReference if you need to pass in a reference to an Activity

2. Anonymous inner classes suggest you write a separate file or

Public void addCallBack(CallBack call){WeakReference<CallBack> mCallBack= new WeakReference<CallBack>(call); }Copy the code

2. Handler

Example:

Class MyHandler extends Handler{private Activity m; public MyHandler(Activity activity){ m=activity; } // class..... }Copy the code

The MyHandler here holds a reference to the activity, causing the GC to not reclaim the memory leak when the activity is destroyed.

Solutions:

+ 1. The use of a static inner class a weak reference 2. In the Activity onDestoty removeCallbacksAndMessages handling in () () @ Override protected voidonDestroy() {
          super.onDestroy();
      if(null != handler){
            handler.removeCallbacksAndMessages(null);
            handler = null;
      }
   }
Copy the code

  1. A static variable

Example:

  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) { } }Copy the code

In this case, the Police holds a reference to the activity, which will cause the activity not to be released, resulting in a memory leak.

Solutions:

//1. sPolice in onDestory () sPolice = null; //2. Strong references to weak references in the Police constructor;Copy the code

4. Non-static inner classes

Refer to the Handler method in point 2

Anonymous inner classes

Example:

  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(); }}; }}Copy the code

Threads and asyncTasks created in this way are anonymous internal class objects. By default, they implicitly hold references to external activities, resulting in memory leaks.

Solutions:

// static inner class + weak reference // write a separate file + onDestory = null;Copy the code

  1. No deregistration or callbacks

Example:

  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 ------
          }
      };
  }
Copy the code

It also causes memory leaks if you do not cancel it while registering the watch mode. For example, observer callbacks that register network requests using Retrofit + RxJava also hold external references as anonymous inner classes, so remember to unregister when you don’t use them or destroy them.

Solutions:

// The unregistered broadcast of onDestory () in the Activity is released @override protected voidonDestroy() {
          super.onDestroy();
          this.unregisterReceiver(mReceiver);
      }
Copy the code

  1. Timing task

Example:

Public class MainActivity extends AppCompatActivity {/** emulation counts */ 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 voidaddCount() { mCount += 1; }}Copy the code

When our Activity is destroyed, it is possible that the Timer is still waiting to execute the TimerTask. The reference it holds to the Activity cannot be collected by the GC, so we cancel the Timer and TimerTask immediately when our Activity is destroyed. To avoid memory leaks.

Solutions:

// When the Activity shuts down, stop all scheduled tasks in progress to avoid memory leaks. private voidstopTimer() {
          if(mTimer ! = null) { mTimer.cancel(); mTimer = null; }if(mTimerTask ! = null) { mTimerTask.cancel(); mTimerTask = null; } } @Override protected voidonDestroy() {
          super.onDestroy();
          stopTimer();
      }
Copy the code

  1. Resources are not closed

Example:

ArrayList, HashMap, IO, File, SqLite, must remember Cursor and other resources out of the clear remove such as close a series of operations on resources.Copy the code

Solutions:

Destroy immediately after useCopy the code

  1. Attribute animation

Example:

Animation can also be a time-consuming task. For example, ObjectAnimator is started in an Activity, but the cancle method is not called when it is destroyed. Although we can no longer see the animation, the animation will continue to play. The control referenced the Activity, which causes the Activity to not be released properly. Therefore, cancel the property animation when the Activity is destroyed to avoid memory leaks.Copy the code

Solutions:

  @Override
  protected void onDestroy() { super.onDestroy(); // Close the animation operation manimator.cancel () when closing the Activity; }Copy the code

10.Android source code or third-party SDK

// If we come across the Android source code or a third party SDK holding our current Activity or other classes during debugging, then what do we do now?Copy the code

Solutions:

// A class or member is found by reflection in Java to perform the manual = null operation.Copy the code

Memory jitter

What is memory jitter

Perhaps the following screen recording can better explain what memory jitter is

As you can see, memory is frequently created and reclaimed when I click on the Button (note the garbage can).

So let’s figure out which part of the code went wrong. Look at the following video

  
     mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { imPrettySureSortingIsFree(); }}); /** *&emsp; After sorting, print a two-dimensional array, one line at a time. Print */ public voidimPrettySureSortingIsFree() {
          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 = ""; Sorted int[] sorted = getSorted(lotsOfInts[I]); // Splice printfor (int j = 0; j < lotsOfInts[i].length; j++) {
                  rowAsStr += sorted[j];
                  if (j < (lotsOfInts[i].length - 1)) {
                      rowAsStr += ",";
                  }
              }
              Log.i("ricky"."Row " + i + ":"+ rowAsStr); }}Copy the code

After the last we are in the onClick imPrettySureSortingIsFree () function inside the rowAsStr + = sorted [j]; Memory jitter caused by String concatenation, because each concatenation of a String requires a new chunk of heap memory. There are actually two better Apis in Java that are friendly to String manipulation, as you might have guessed. Replace String with StringBuffer or StringBuilder, and you’ll get rid of the jitter caused by String concatenation perfectly.

The modified

/** *&emsp; Print a two-dimensional array, one line at a time. Print */ public voidimPrettySureSortingIsFree() {
              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(); StringBuilder sb = new StringBuilder(); String rowAsStr ="";
              for(int i = 0; i < lotsOfInts.length; I++) {// delete the previous line sb.delete(0, rowasstr.length ()); Sorted int[] sorted = getSorted(lotsOfInts[I]); // Splice printfor (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); }}Copy the code

Here you can see that no garbage can appears, indicating that memory jitter is resolved.

Note: In real development finding these logs in LogCat means memory jitter has also occurred (concurrent Copying GC Freed…. appears in logs)

Recycling algorithm Mark cleaning algorithm Mark-sweep Copying algorithm Mark compression algorithm Mark-Compact generational collection algorithm

Summary (as long as this habit is formed, at least 90% can be avoided without causing memory exceptions)

1. Data types: Do not use basic data types that take up more space than you need

2. Use foreach as much as possible, use iterator less, and use automatic boxing as little as possible

3. Solution processing of data structure and algorithm (array, linked list, stack tree, tree, graph)

  • Sparse arrays (keys are integers) can be used for data sizes up to a thousand levels, and ArrayMaps (keys are objects) are less efficient than HashMaps but less memory-saving.

4. Enumeration optimization

Disadvantages:

  • Each enumerated value is a singleton, and using it adds extra memory consumption, so enumerations take up more memory than integers and strings
  • Using Enum more will increase the size of DEX file, which will cause more IO overhead at runtime, and make our application need more space
  • Especially for large apps with multiple Dex, initialization of enumeration can easily lead to ANR

Optimized code: Can directly limit the number of arguments passed

  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() {returnthis.value; }}Copy the code

5. The problem is static

  • Static is initialized by the compiler calling the Clinit method

  • Static final does not need to be initialized. It is packaged in the dex file and can be called directly. It does not require memory allocation during class initialization

Members of basic data types can be written as static final

6. Minimize the use of += in string concatenation

7. Repeated memory application fails

  • Multiple calls to the same method, such as recursive functions, callbacks to new objects in functions

  • Do not refresh the UI in onMeause() onLayout(),onDraw() (requestLayout)

8. Avoid GC collecting objects to be reused in the future (Memory design pattern object pool + LRU algorithm)

9.Activity component leaks

  • Do not pass the activity context as a parameter for non-business purposes; pass the application context instead

  • Non-static inner classes and anonymous inner classes hold activity references (static inner classes or separate writes to files)

  • Callbacks in singleton mode hold activity references (weak references)

  • Handler. PostDelayed ()

    . If the open thread needs to pass in parameters, weak argument receiving can solve the problem. Handler remember clear removeCallbacksAndMessages (null)

10. Use IntentService instead of Service for time-consuming operations

Finally, mind mapping makes a summary: