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
- 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
- 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
- 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
- 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
- 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(); }}); /** *  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
/** *  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: