The problem

background

Based on the analysis of logs collected online, some OOM logs exist. Therefore, you can trace memory leaks through LeakCanary.

Reference chain log

During development, leakCanary reported a memory leak and logs the details as follows:

Other activities have the same memory leak, and the log is as follows:

MAT was used for analysis, and the reference chain was the same. The log is as follows:

impact

AS Profiler analysis shows that this memory leak is very serious, and the GC cannot collect the instance after the page Finish falls. In this way, if the user attempts the page more than once, it is easy to trigger the OOM exception.

Analysis of the

The structure of a FileMainActivity is as follows: An Activity contains a Fragment, which is a list.

The ViewRootImpl$ViewRootHandler causes the Activity not to be recycled by the reference chain. It was initially suspected whether it was caused by handler. The FileMainActivity handler uses static internal classes and soft reference holders.

Through the search engine, there is no relevant blog introduction. The problem stalled for a while.

However, in view of the seriousness of the problem, we decided to adopt the elimination method for verification. Of course, the process takes a long time. The main ideas are as follows:

1, The Activity does not integrate Fragment, and then run, enter and exit repeatedly, analyze GC, find no memory leak; (Code in Fragment has memory leak)

2, restore the Fragment code, onActivityCreate code is approximately: initialize the controller, request network data, display the list. By masking the three methods in turn, then enter the validation (problem found after masking the display list method, there is no memory leak. The problem is located in the way the list is displayed.

3. By sequentially masking the display list, the problem is located in one line of code, causing a memory leak

mPullToRefreshLayout.refreshComplete();
Copy the code

4, mPullToRefreshLayout is a third-party drop-down refresh control, PtrFrameLayout. RefreshComplete is available as follows:

/**
     * Call this when data is loaded.
     * The UI will perform complete at once or after a delay, depends on the time elapsed is greater then {@link #mLoadingMinTime} or not.
     */
    final public void refreshComplete() {
        if (DEBUG) {
            PtrCLog.i(LOG_TAG, "refreshComplete");
        }

        if(mRefreshCompleteHook ! = null) { mRefreshCompleteHook.reset(); } int delay = (int) (mLoadingMinTime - (System.currentTimeMillis() - mLoadingStartTime));
        if (delay <= 0) {
            if (DEBUG) {
                PtrCLog.d(LOG_TAG, "performRefreshComplete at once");
            }
            performRefreshComplete();
        } else {
            postDelayed(new MyRunnable(this, 1), delay);
            if (DEBUG) {
                PtrCLog.d(LOG_TAG, "performRefreshComplete after delay: %s", delay); }}}Copy the code

5. There is a critical code postDelayed, so it is suspected that there may be a memory leak in this code. Print breakpoints to find the following:

The final location problem was as follows: Due to the postDelayed memory leak in the refreshComplete method of PtrFrameLayout, the Activity was not collected due to the long delay.

To deal with

After several twists and turns, finally locate the problem. So now it’s easy to deal with. Through the analysis, we found that the main problem was caused by the large delay value. Our analysis found the following code

  int delay = (int) (mLoadingMinTime - (System.currentTimeMillis() - mLoadingStartTime));
Copy the code

We found that long was forced to int, which caused a data overflow and affected delay’s final computation. The memory leak can be fixed by doing the following:

After the fix, proflier analysis shows that the Activity is properly collected by GC, and the problem is resolved.

conclusion

1. Analysis of memory leaks requires multiple tools, including leakcanary and the Profile and Mat tools delivered with THE AS

2. It takes patience and care. Optimizing memory leaks can be a hassle, but it’s also important

recommended

Android Performance Optimization – Memory Leaks (part 1)

Android Performance Optimization – Memory Leaks (part 2)

about

Welcome to pay attention to my personal public number

Wechat search: Yizhaofusheng, or search the official ID: Life2Code

  • Author: Huang Junbin
  • Blog: junbin. Tech
  • GitHub: junbin1011
  • Zhihu: @ JunBin