您的位置:首页 > 移动开发 > Android开发

Android Context导致的内存泄漏分析(示例代码+分析工具使用)

2016-10-26 12:08 686 查看
Android开发中因为有限的内存,以及防止OOM问题出现,解决内存泄漏问题将是开发者一直持续下去的工作。本文就分析了不当使用(持有)context导致的内存泄漏。


1. 为什么使用Context有可能会导致内存泄漏?

首先从context的本质谈起,context名称上代表了上下文,实质上是Application、Activity或Service的一个引用。因此如果有生命周期较长的对象,比如线程持有了一个context引用,那么在线程结束前,这个context是无法得到释放的,这也意味着context代表的activity、service无法被GC回收,这就发生了内存泄漏。


2. 来个例子

还记得屏幕旋转,会销毁当前Activity,重新创建一个新的Activity的事吗?我们就以这个操作来做泄漏内存的示例,原理是让老的Activity无法被销毁。
private static Drawable mBackgroudImage;

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

TextView label = new TextView(this);//持有context
label.setText("演示旋转屏幕导致内存泄漏");

if (mBackgroudImage == null) {
mBackgroudImage = getDrawable(R.drawable.beauty); //这个是随便在网上找的一张图片
}
label.setBackgroundDrawable(mBackgroudImage);

setContentView(label);
}


好,运行起来,将屏幕旋转几次,这个代码已经泄漏了老的Activity。



是不是内心有点小怀疑?

我们往下看截图可以证明。


3. 使用Android Monitor验证内存泄漏情况

怎么验证,是否发生了内存泄漏呢?

我们可以通过Android Studio提供的Memory Monitor工具来观察Java Heap情况。

打开Android Monitor,如图所示,我们旋转几次屏幕后,点击“Dump Java Heap”按钮



接下来会,弹出一个hprof编辑框,在右边的红圈部分点开“分析界面”



好,在新弹出的分析界面,点击绿色箭头开始分析,下半部分就是分析结果。



注意我红圈标注出来的地方,发现SplashActivity竟然有2个实例!!!!这就证明确实发生了内存泄漏,有一个SplashActivity泄漏了。


4. 分析原因。

首先我们发现,这段代码定义了一个没有初始化的静态Drawable变量。众所周知,静态变量是属于一种跟“类”而非“实例”绑定在一起的对象。是一个被所有实例共享的成员变量,当给它赋值的时候,实际上是赋值给了整个“类对象”。

出于省事的考虑,代码中并没有在每次onCreate中去加载赋值mBackgroudImage,而是检测它如果不为空,就不再赋值。

根据官网的说法,将一个Drawable对象赋给某个View的时候,这个View同时也作为一个callBalk被Drawable对象给引用了。即,Drawable对象持有对应View的引用。这个可以看setBackgroundDrawable()源码,确实是没错的。

@Deprecated
public void setBackgroundDrawable(Drawable background) {
//省略其他内容,可以看到确实View
//作为一个callBalk被Drawable对象给引用了

// Set callback last, since the view may still be initializing.
background.setCallback(this);
}


事实上,当我们第一次运行起来SplashActivity时,会给mBackgroudImage赋值,当屏幕旋转的时候则不会第二次赋值。因此,mBackgroudImage仍旧持有第一次的TextView的引用。

而TextView的新建需要传入一个context,因此它持有了SplashActivity的一个引用。

所以,相当于静态变量mBackgroudImage间接的、始终持有第一个创建出来SplashActivity没有释放。

所以就发生了内存泄漏!!!


总结

通过本文的分析,让一个生命周期长于Activity的对象持有context引用很容易就发生了内存泄漏,开发者需要特别警惕!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息