Why do memory optimizations? First, because the interviewer likes to ask, second, to avoid OOM!
Here’s what happens in OOM:
Do I have enough memory to create an object? Enough! Ok create go; Not enough! Garbage collection. It’s done. Is that enough now? Enough! Ok create go, not enough! Throw an OOM exception.
So, OOM happens if there is not enough memory after garbage collection! So how to avoid it? The first is not to create objects that big, that is, to avoid creating large objects; The second is to have enough memory after garbage collection, that is, to avoid memory leaks.
The big object
The big object here is a general reference, a general reference to a single big object, or a collection of small objects. We know that the size of an object in memory is largely determined by the size of its member variables. Therefore, if we only need some internal data of an object, we should try not to create or hold the object, but only hold the part we need, for example:
public class Music {
String songName;
String singer;
String lyric;
Date date;
String icon;
//...
}
// 只需要展示歌词
public class LyricActivity {
// 但是却持有了整个Music
public class Music;
}
Copy the code
For example, we only need lyric for Music, but we let the Activity hold the whole Music. We also know that the life cycle of the member variable is the same as that of the object that holds it, so the Music will live until the Activity is collected, and the garbage collection will not be able to collect it, which will consume memory. This is not recommended. Therefore, we should only take what we need, and don’t hold what we don’t need, which can avoid OOM and conform to the least knowledge principle.
Bits and pieces of small objects
We also want to avoid creating a lot of small objects. For example, we all write layout files. In an XML file, to display a control, we can use a combination of methods, such as image above and button below.
<LinearLayout oritation="vertival">
<ImageView/>
<Button/>
</LinearLayout>
Copy the code
Of course, but we need to create 3 views, we can completely define a View, such as inherit from the system Button, draw a picture on the top, so that only one View can be, directly use 2/3 less memory. There is also object reuse, such as RecyclerView, which has its own cache to ensure that the item object created is only enough for screen display at most, which is also to avoid a large number of persistent small objects. Small objects, though small, will also create OOM.
Bitmap
The most important is Bitmap. Most people know that the image given by UI is compressed, but the compressed space is its own storage space. At best, it optimizes the size of APK, but does not save runtime memory.
width * height * 4byte
Copy the code
Since bitmaps are loaded with ARGB_8888 by default, i.e., 32 bits, i.e., 4 bytes, a 1000*1000 image occupies the following memory size: 1000 * 1000 * 4 = 4_000_000byte, which is 4M, and an int is only 4byte, which is equivalent to a million ints, 500,000 longs, which is equivalent to a String with a length of 2 million, so Bitmap is the biggest cause of OOM. When we compress images, we must remember to crop; If you can use drawable, don’t use Bitmap. If you can’t set the background, don’t set the background. For example, a View, 90% of it is covered by the View, then there is no need to use Bitmap for the background. The images in the ImageView can be cropped as small as possible without affecting the visual quality of the image. The images in the ImageView should not be loaded with ARGB_8888.
Local variables and code blocks
We also need to optimize the local variable. We know that the local variable is not recycled until the method is called, because it is GCROOT. For example:
Public void test(){//1 create a big object BigObject big = getBitObject(); String name = big.xxx; //big = null; //2 User user = new User(); user.name = name; / /... //3 Create an object, run out of memory, GC is required, but big is not collected because the current method is still running. TestObj obj = new TestObj(); }Copy the code
GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot = GCRoot Equivalent to prematurely ending its life cycle, the best way to do this is with code blocks such as:
public void test(){ User user = new User(); {//1 create a big object BigObject big = getBitObject(); String name = big.xxx; } // The code block ends, the internal local variable life cycle ends, can be recycled. user.name = name; / /... //3 Create an object, run out of memory, GC is required, but big is not collected because the current method is still running. TestObj obj = new TestObj(); }Copy the code
When a code block is added, the life cycle of variables in the code block automatically ends when the code block ends, and the garbage collection is directly collected. This is equivalent to limiting the life cycle of an object, and we should use code blocks wisely.
- Object obj = new Object(); Obj is strong reference. Never recycled, not even the OOM
- SoftReference: SoftReference. If the memory is insufficient, the storage is reclaimed again.
- WeakReference: WeakReference is recycled when it is encountered.
- A virtual reference is used to tell an object whether or not it was reclaimed. This is used in conjunction with the ReferenceQueue, and if it can be found in the Queue, it is reclaimed.
Let’s simulate a process:
- 1 User user = new User(); Creates an object and finds that it is out of memory
- 2 GC, encounters obj1, finds that it is a strong reference, and skips.
- 3 hits obj2, it’s a soft reference, mark it, and keep going.
- 4 runs into obj3 and recycles it.
- Create user (‘ user ‘);
- 6 Object scan finished, memory insufficient, reclaim the soft reference just marked, reclaim the soft reference.
- 7. Create user.
- If 8 is not enough, throw OOM. Then obj1 is a strong reference and is not recycled.
From the above we know that strong references are never recycled; A soft reference is a secondary collection when the memory is insufficient. In other words, when the memory is insufficient, the soft reference is first reclaimed, and then reclaimed when the memory is insufficient. Weak references, on the other hand, are reclaimed when they are encountered (even if there is enough memory).
We also know that soft and weak references are collected when memory is out of order (either once or twice), so there is no memory leak. Therefore, only strong references can cause memory leaks, and memory leaks are: GCRoot = GCRoot = GCRoot = GCRoot = GCRoot
- Static variables and constants in the method area
- 2 Local variables that the vm stack is referencing
- Member variable of the active thread
In other words, as long as an object is not referenced by the above variables, there is no memory leak. To be clear, you can avoid memory leaks by not letting long-life objects hold short-life objects (static objects hold non-static objects, app level objects hold Activity level objects), and if you do need to hold objects, disconnect references when the short-life objects end.
This process requires all threads to pause, of course, a very short moment, but if the garbage collection is frequently triggered, then many a mickle makes a mickle, it will cause a significant lag, this is called memory jitter, so what can cause memory jitter? The answer is: create lots of objects in a short amount of time. Such as:
for (int i = 0; i < 1000; i++) { User user = new User(); / /... }Copy the code
We create objects 1000 times, and on the 100th time, we run out of memory and GC happens, and on the 200th time, we run out of memory and GC happens… And so on, with 10 GCS, the memory graph looks like an up and down mountain, called jitter. This leads to frequent collection, which in turn stops all threads, causing significant stalling, which should be avoided.