?? In the previous article several cases of Android memory leak were mentioned in the development of memory leaks common, but too hasty. Since the beginning of the year, the work has not yet formally started, I looked at GitHub open source Square leakcanary, and the company's project test environment to practiced hand, trying to find out the memory leaks in the project. Unlike the previous one, I'll start by talking about the memory area of Java and the garbage collection mechanism, and then talking about the Leakcanary application, and ending with a real case that I've encountered in the project.
The memory model of Java
?? For Leakcanary, we are primarily concerned with the heap and stack of Java program runtime. A heap is a place where objects are stored, and stacks are used to store references. References are persisted with the object through the handle of the object or the address of the object. Garbage collection takes place on the heap.
Java garbage Collection algorithm
?? There are a number of garbage collection algorithms, and here are some of the common garbage collection algorithms in Java:
The garbage collector (GC) takes some of the referenced objects on the stack as the root node (GC root), and according to these references, searches for the objects associated with them, and the path of the searched nodes is called the GC chain. For example, there are three classes of A,b,c, where a holds the application of B, B holds a reference to C,
publicclass A { publicA(B b) { this.b = b; } private B b;}publicclass B { publicB(C c){ this.c = c; } private C c;}publicclass C {}
When executing:
new C(); B b new B(c); new A(b);
We can find the object of C by referencing a, and this chain can be used as a GC chain.
?? When an object has a path from the GC root, it indicates that the object is being referenced. The GC is "spared" for such objects. If an object does not have any GC root, the GC will mark the objects so that they can be recycled later.
?? Speaking of which, it is necessary to introduce a memory leak. When an object's "mission accomplished", according to our wishes, at this time the GC should reclaim the memory space of this part of the object. For example: A method contains a local variable A, and when this method is finished, we want a to be recycled soon, but for some reason it is not recycled and we say a memory leak has occurred. Why is there a memory leak? The bottom-end is that it is accessible from GC root to this object. For Android, many of Android's components have a life cycle concept, such as: Activity,fragment. These components should be reclaimed when the lifecycle of these components ends (the OnDestroy method is called back). But for some reason, for example, activity is referenced by an anonymous inner class with a long life cycle, referenced by a static object, referenced by handler (typically called by handler Postdelay method) ... and other conditions.
?? Android has a size limit on the memory footprint of each process, previously within 16MB, which requires careful use of memory. Memory leaks cause objects and even Android components (often containing many other references, large memory consumption) can not be recycled, the program security is a great danger, it is possible for the user in a memory leak action on the repeated operation, so that within a very short period of time the rapid expansion, and finally caused the program flash "tragic outcome ”。 However, this outcome is not what we want, so we should try our best not to let the program create a memory leak. Due to memory leaks, it is not as straightforward as a null pointer error, and it is difficult for ordinary programmers to discover the pitfalls of memory leaks. According to statistics, 94% oom anomalies are caused by memory leaks. So, resolving memory leaks is a topic that our Android programmers must face.
Memory Leak detection Artifact leakcanary
?? Leakcananry is an open-source product for the open source square, which detects memory leaks in programs. Easy to get started, easy to operate, is the vast number of Android programmers must artifact.
GitHub Project address.
Integrated Leakcanary
?? Because the company project is still developed on Eclipse, it's about how to integrate within Eclipse.
?? First we download the leakcanary for Eclipse. The project address. Thank the author for his hard work here.
?? We then import the downloaded package into the Eclipse workspace at Eclipse. Use it as a library for Android.
?? Next, we'll copy the service and activity inside the leakcanary into your project. Remember to change the name of the service and activity to the class name. The modified manifest file is roughly:
.................. A list of your projects ...<!--leakcanary Required interface and services-- <serviceandroid:name="Com.squareup.leakcanary.internal.HeapAnalyzerService" android:enabled="false"android:process=": leakcanary" /> <service android:name= "Com.squareup.leakcanary.DisplayLeakService" android:enabled =/> <activityandroid:name="Com.squareup.leakcanary.internal.DisplayLeakActivity" android:enabled="false"android:icon="@drawable/__leak_canary_icon" Android:label="@string/__leak_canary_display_activity_label"android:taskaffinity= "Com.squareup.leakcanary" android:theme="@style/__leakcanary.base" > <intent-filter> <action android:name="Android.intent.action.MAIN" /> <category android:name="Android.intent.category.LAUNCHER" /> </intent-filter> </activity>
At this point, leakcanary integration is complete.
Using Leakcanary in your project
We need to initialize the leakcanary in the application and then monitor the class in the OnDestroy of Baseactivity or basefragment. The code is:
/** * 初始化内存泄露监测 applicaton里面的代码 */ privatevoidinitRefWatcher() { this.refWatcher = LeakCanary.install(this); } //BaseActivity或者BaseFragment的代码 @Override protectedvoidonDestroy() { super.onDestroy(); RefWatcher refWatcher = MentorNowApplication.getRefWatcher(this); refWatcher.watch(this); }
In this way, we can test our project.
Case List
Below, I take a memory leak in our project to explain the specific use (provided that your project correctly integrates the Leakcanary). I pasted out the code that had the memory leak and pasted the modified code.
Code that has a memory leak:
In the project, we used the time bus eventbus to decouple and, as we all know, using Eventbus we need to register first, when the page is destroyed, we should first anti-registration, which is due to the specific design of Eventbus, The life cycle of the Eventbus is the same as the entire application life cycle. Below, I use Leakcananry to detect fragement memory leaks caused by non-registration. The log information obtained through Leakcananry is as follows:
02-17 14:40:10.219:d/leakcanary (29354): * Com.mentornow.MainActivity has leaked:
02-17 14:40:10.219:d/leakcanary (29354): * GC ROOT static event. Eventbus.defaultinstance
02-17 14:40:10.220:d/leakcanary (29354): * References event. Eventbus.typesbysubscriber
02-17 14:40:10.220:d/leakcanary (29354): * References java.util.HashMap.table
02-17 14:40:10.220:d/leakcanary (29354): * References Array Java.util.HashMap H ashM aPE NT R y[].[3] Geneva? - -: +:10.220:D/L eak C aNaR y(29354):?R eF eR eNCesJ ava.uT Il .H ashM aP Hashmapentry.key
02-17 14:40:10.220:d/leakcanary (29354): * References COM.MENTORNOW.FRAGMENT.DISCOVERFRAGMENT.GV
02-17 14:40:10.220:d/leakcanary (29354): * References Com.mentornow.view.MyGridView.mContext
02-17 14:40:10.220:d/leakcanary (29354): * Leaks com.mentornow.MainActivity instance
02-17 14:40:10.220:d/leakcanary (29354): * Reference KEY:FEF0C426-0096-475B-9F5C-CB193FA7CECD
02-17 14:40:10.220:d/leakcanary (29354): * Device:motorola Motorola XT1079 Thea_retcn_ds
02-17 14:40:10.220:d/leakcanary (29354): * Android version:5.0.2 api:21 leakcanary:
02-17 14:40:10.220:d/leakcanary (29354): * durations:watch=5042ms, Gc=196ms, heap dump=2361ms, analysis=26892ms
Analysis Log
The first sentence clearly tells us that a memory leak has occurred in mainactivity.
The second sentence causes a memory leak because the reference defaultinstance from Eventbus to Mainactivity is accessible.
The following sentence is the node of this GC chain:
Eventbus first causes discoverfragment not to be recycled, because Discoverfragment retains mainactivity references (via framgnet.getactivity ()), So from Eventbus to Mainactivity is up to the.
Because gcroot to mainactivity are accessible, the GC does not reclaim mainactivity, which results in memory leaks.
Solutions
In accordance with Eventbus's usage specification, we should use the anti-registration after we have finished using it. We call the fragment method inside the OnDestroy method, and then run the program. found that the previous log no longer prints.
Summarize
When I was troubleshooting a memory leak with my company project, I found that memory leaks were often overlooked. So, I'm going to summarize some of the scenarios where memory leaks can occur:
1, the handler is used, and the delay operation is used. Like a carousel diagram.
2, threads are used. Threads generally handle time-consuming operations, and the execution time of the sub-thread portion may detect the life cycle of the page, and memory leaks can occur if not processed in the thread. The workaround is to use a virtual reference to terminate the thread when the page is destroyed, and so on.
3, anonymous internal classes are used. Because anonymous inner classes keep references to external classes, it is important to use anonymous inner classes in activity or fragment in order not to allow the life cycle of the inner class to be greater than the life cycle of the outer class. Or use a static inner class.
4, the incoming parameter is wrong, because the project uses the Friend Alliance pushes, exposes the API is Umengpushagent this class maintains a static context, if enters the activity, will occur the memory leak.
And so on, memory leaks are common, and the memory leaks in the system SDK are also detected when using Leakcanary. In order to program health, robust operation, find out and solve the memory leak problem is an optimized way.
Probe into Memory leak detecting artifact--leakcanary