Android Performance: Memory Monitor & Heap Viewer
Memory Monitor可以监视应用的内存使用,并以可视化的方式显示出来。Heap Viewer可以实时地报告应用在内存中分配的对象的类型、数量和在堆上占用的空间。二者都是分析应用内存分配和管理的工具,能够帮助定位内存泄漏等问题。
下面例子使用的代码可以在这里找到,运行后点击“MEMORY LEAKS”,如图1所示。
Contents
1. Memory Monitor
在Android Studio中打开Android Monitor面板,选择Monitors选项卡,可以看到在Memory一栏记录了当前应用的内存使用情况。深蓝色背景的区域为应用占用的内存,浅蓝色背景的区域为系统分配给应用的可用内存。反复旋转设备,使屏幕发生旋转,可以看到随着屏幕反复旋转,应用占用的内存不断增加,内存占用量增加到一定程度之后,发生垃圾回收,内存占用突然下降,如图2所示。
继续旋转屏幕,可以观测到后续的内存回收,如图3和图4。
从图3和图4可见,尽管经过了垃圾回收,但应用消耗的内存不断增加。在内存回收时,淡蓝色的区域也提高了,说明系统为应用分配了更多的可用内存。
2. Heap Viewer
打开Android Device Monitor (Tools -> Android -> Android Device Monitor),如图5所示,在左侧Devices下选择要查看的应用com.example.android.mobileperf.compute(1),选中工具栏的Update Heap(2),点击右上的DDMS页面(3),再点击右侧边栏的Heap(4),打开Heap Viewer界面。反复旋转屏幕,当垃圾回收发生后,Heap的状态会实时更新(5),可见当前分配了2.873MB内存,垃圾回收释放了1.916MB内存。
继续反复旋转屏幕,Heap Viewer会在垃圾回收发生时更新,如图6、图7、图8所示。
可见应用占用的内存在不断增加。
从Memory Monitor和Heap Viewer都可以看到,随着不断地屏幕旋转,虽然垃圾回收会正常进行,但应用占用的内存会不断增加,很可能存在内存泄漏问题。屏幕旋转会销毁并重新创建Activity,一个很容易发生的问题是在Activity销毁时还保留了某些引用,导致有对象残留了下来,造成内存泄漏。
在MyCustomView.java可以找到如下一段代码:
private void init() { ListenerCollector collector = new ListenerCollector(); collector.setListener(this, mListener); }
其中ListenerCollector定义如下:
public class ListenerCollector { // A common case is to want to store all the listeners for a particular type of view // somewhere. Harmless AND convenient. Or... is it? o_0 static private WeakHashMap<View, MyListener> sListeners = new WeakHashMap<View, MyListener>(); public void setListener(View view, MyListener listener) { sListeners.put(view, listener); } }
这里相当于把每个mListener
都保存了起来,当屏幕旋转,Activity被销毁时,mListener
并没有被释放,导致内存泄漏。应当在Activity结束时释放掉相关的Listener,如:
@Override protected void onStop() { super.onStop(); // By clearing out the set of listeners when the Activity stops, we can avoid excessive // memory leaks. When the Activity is re-started, the custom view will be created and // will then create its own listener again. ListenerCollector.clearListeners(); }