LeakCanary 的几个 bug

神器 LeakCanary 并不是完美的,它仍然有一些问题待完善。

检测不到内存泄漏的 bug

1163

问题描述:

使用 LeakCanary version: 1.6.2 / master cefc33d 检查如下代码中的内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity extends Activity {

static class Lol {
static Object foo;
}

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Lol.foo = this;

}
}

检查结果:

  • On API 26, no leak is detected
  • On API 21, the leak is detected

问题原因:

Snapshot.getGCRoots() only returns the GC Roots from the first heap. In both API 21 and API 26, we had 4 heaps, however on API 21 only the first heap has GC Roots, while on API 26 the first 3 heaps had GC Roots. The missing gc roots lived in subsequent heaps.

最新版本的 LeakCanary 中已修复这个问题。

Fragment View 内存泄漏问题

1061

问题描述:

如下代码中,如果没有在 onDestroyView 中及时清理 leakingView,就会产生内存泄漏问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ViewLeakingFragment extends Fragment {

public static void addToBackstack(final TestActivity activity) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override public void run() {
activity.getSupportFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.fragments, new ViewLeakingFragment())
.commit();
}
});
}

private View leakingView;

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return new View(container.getContext());
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Leak: this fragment will stay in memory after being replaced, leakingView should be cleared
// onDestroyView()
leakingView = view;
}
}

Fragment view 内存泄漏容易发生的两个时机:

  • replace + addToBackStack fragment transaction is executed.
  • This also occurs when you use detach regardless of the backstack.

最新 LeakCanary 中已增加对 fragment 以及 fragment view 内存泄漏的检查。