Proficiency in industry is more than diligence and playfulness, writing articles to practice expression skills, and writing codes to practice basic skills.
Summary of OOM and memory optimization
What is OOM?
OOM ( java.lang.OutOfMemoryError ), JVM does not have enough memory to allocate space for the object, exceeding the maximum heap space of jvm (-Xmx parameter), this exception will be triggered, resulting in the application being forcibly killed.
OOM reasons?
For java programmers, we generally just create objects, and we seldom worry about the recycling of objects, because the JVM has a garbage collector to perform GC regularly, and is responsible for recycling useless objects that have not been referenced to release memory. However, our application still has memory leaks or memory overflows, which are also two major factors that lead to oom.
Memory leak and memory overflow?
- Memory leak refers to the previously allocated memory space, which is no longer used, but cannot be released by the garbage collector. If this situation keeps piling up, it will lead to memory overflow.
- Out of memory, not enough memory to allocate space, not enough space.
In which area does OOM occur?
Let's first understand some jvm memory models:
According to the JVM specification, except for the program counter area, OOM may appear in other areas.
jvm memory allocation and recovery mechanism
We usually write java code, and most of the OOM occurs in the heap area, so we focus on understanding the memory model and recycling mechanism of heap space objects.
- The heap space is divided into new generation (eden + S0 + S1), old generation (Tenured) and permanent generation (Permanent). The permanent generation java1.8 has been canceled and changed to metaspace.
- Newly created objects are basically allocated in the new generation eden area, and large objects are directly allocated to the old generation.
- After a GC, the surviving objects in the eden area are moved to S0; the surviving objects in S0 are moved to S1; the surviving objects in S1 are moved to Tenured
- Every GC, the age of the object increases by one year, reaching 15 years old (default, configurable), and promoted to the old age.
The Judgment Algorithm for Object Recycling
- Reachability analysis: Use GC Root as the starting point of the analysis to find references to objects. If an object is found that cannot be directly or indirectly referenced by GC Root, then the object can be recycled.
- Reference counting method: Add a reference counter to the object. Whenever there is a reference to it, the counter value is increased by 1; when the reference becomes invalid, the counter value is decreased by 1.
Garbage Collection (GC)
Garbage collection algorithm :
- mark-clear
- mark-organize
- copy
Garbage collection mainly works in the new generation and the old generation. In the generational collection model (above), different generations use different collection algorithms:
- eden and Survivor: usually replication algorithms
- Tenured: usually a markup algorithm, android is a CMS
Memory Limits for Android Apps
-
Java Heap: The memory requested by Java is limited by the size of vm.heapsize.
-
native Heap: The memory requested by the c/c++ layer is not limited by the size of vm.heapsize.
How to modify:
Code location/frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
/*
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
*/
parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
}
The source of Android throwing OOM
heap allocation failed
Code location: /art/runtime/gc/heap.cc
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
// If we're in a stack overflow, do not create a new exception. It would require running the
// constructor, which will of course still be in a stack overflow.
if (self->IsHandlingStackOverflow()) {
self->SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
return;
}
std::ostringstream oss;
size_t total_bytes_free = GetFreeMemory();
oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
<< " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << " until OOM";
// If the allocation failed due to fragmentation, print out the largest continuous allocation.
if (total_bytes_free >= byte_count) {
space::AllocSpace* space = nullptr;
if (allocator_type == kAllocatorTypeNonMoving) {
space = non_moving_space_;
} else if (allocator_type == kAllocatorTypeRosAlloc ||
allocator_type == kAllocatorTypeDlMalloc) {
space = main_space_;
} else if (allocator_type == kAllocatorTypeBumpPointer ||
allocator_type == kAllocatorTypeTLAB) {
space = bump_pointer_space_;
} else if (allocator_type == kAllocatorTypeRegion ||
allocator_type == kAllocatorTypeRegionTLAB) {
space = region_space_;
}
if (space != nullptr) {
space->LogFragmentationAllocFailure(oss, byte_count);
}
}
self->ThrowOutOfMemoryError(oss.str().c_str());
}
Commonly used memory analysis commands
adb shell dumpsys meminfo [package name]
adb shell procrank
Memory usage leaderboard, failed to try, need to upload sh by yourself.
adb shell cat /proc/meminfo
adb shell free
total = used + free
adb shell top
Memory Leak Analysis Tool
Memory Analyzer Tool
1. Grab the memory application snapshot hprof file
adb shell am dumpheap com.xxx.xxx /data/local/tmp/14_54.hprof
2. Convert to standard hprof
hprof-conv 14_54.hprof 14_54_R.hprof
hprof-conv is in the platform-tools directory of the sdk
3. Use mat to open
The home page will have the current guess of memory leaks, large objects, and objects with a large number of objects will be set as suspect targets.
Analyzing memory leaks
Large object analysis
Click Overview - Dominator, the objects are sorted according to the size of the occupied space
Focus on checking the large objects listed above. In the figure below, it is obvious that there are multiple identical activities that keep referencing the large objects and cannot be released, resulting in memory leaks.
GC Root reference chain view
In the figure above, select an item, right-click -> Paths to GC Roots–>exclude all phantom/weak/soft etc reference, and focus on analyzing strong references.
You can see the context reference chain and locate it in your own code. In this example, a class statically holds the context of the Activity. After the Activity is destroyed, the context cannot be released, causing a memory leak.
Histogram comparison
If the problem is that the memory grows slowly over time, and a single hprof cannot see it, then take a snapshot again after a while, compare the two snapshot files, and see which object has been growing.
The hprof that analyzed the problem before could not be found, so I found two temporarily. The above picture is for reference only, and the actual operation shall prevail.
Android Profile
Open the Profile page, select the monitored program, and click the MEMORY–>dump button.
You can also view hprof on android studio for analysis.
LeakCanary
quote
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
}
transfer
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
}
example
public class MainActivity extends AppCompatActivity {
private static int k = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(runnable).start();
}
public Runnable runnable = new Runnable() {
@Override
public void run() {
while (true){
MainActivity.k++;
Log.d("TAG",""+k);
}
}
};
}
A memory leak occurs, go to this page to view it directly.
How to reduce the image size? cry
Memory leak, summary of common scenarios and solutions of memory overflow
The following excerpts are summarized by others and are here for backup.
1. The resource is not closed
When the resource object is no longer used, its close() function should be called immediately to close it, and then set it to null.
For example , unclosed resources such as Bitmap will cause memory leaks. At this time, we should close them in time when the Activity is destroyed.
2. The registration has not been cancelled.
For example, the memory leak caused by the unregistered BroadcastReceiver and EventBus, we should log out in time when the Activity is destroyed.
3. Static variables of the class hold large data objects
Try to avoid using static variables to store data, especially large data objects, it is recommended to use database storage.
4. Memory leak caused by singleton
Prioritize the use of the Context of the Application. If you want to use the Context of the Activity, you can use a weak reference to
encapsulate Then, get the Context from the weak reference where it is used. If you can’t get it, just return it directly. .
5. Static instances of non-static inner classes
The life cycle of this instance is as long as that of the application, which causes the static instance to always hold the reference of the Activity, and the memory resources of the Activity
cannot be recovered normally. If you need to use Context, try to use Application Context.
6. Handler temporary memory leak
After the Message is sent, it is stored in the MessageQueue. There is a target in the Message, which is a reference to the Handler
. If the Message exists in the Queue for too long, the Handler cannot be recycled. If the Handler is non-static,
the Activity or Service will not be recycled. And the message queue is constantly polling and processing messages in a Looper thread.
When the Activity exits, there are still unprocessed messages or messages being processed in the message queue, and the Message in the message queue
holds a reference to the Handler instance. The Handler also holds a reference to the Activity, so the memory resources of the Activity cannot be recovered in time
, causing a memory leak. The solution looks like this:
1. Use a static Handler internal class, and then use a weak reference to the object (usually Activity) held by the Handler, so that
when recycling, the object held by the Handler can also be recycled.
2. When the Activity is Destroyed or Stopped, the messages in the message queue should be removed to avoid
unprocessed messages in the message queue of the Looper thread that need to be processed.
It should be noted that AsyncTask is also a Handler mechanism, which also has the risk of memory leaks, but it is generally temporary. For
memory leaks like AsyncTask or threads, we can also separate the AsyncTask and Runnable classes or use static
inner classes.
7. Memory leaks caused by objects in the container not being cleaned up
Before exiting the program, clear the items in the collection, then set them to null, and then exit the program
8、WebView
WebView has the problem of memory leaks. As long as the WebView is used once in the application, the memory will not be released. We can
start an independent process for WebView, use AIDL to communicate with the main process of the application, and the process where WebView is located can
be destroyed at an appropriate time according to business needs, so as to achieve the purpose of releasing memory normally.
9. Memory leak caused by using ListView
When constructing the Adapter, use the cached convertView.
If there is an error, please help point out.
reference
OOM of Android memory optimization
User Guide - Android app performance optimization summary (memory performance optimization)