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

Android 最佳性能实践之内存管理(二)

2016-05-29 21:22 429 查看
上一篇文章针对官方文档做了一些翻译整理工作,主要针对是原理层面进行分析,第二篇主要是怎么做。已经做了一小部分翻译,然后想起了guolin郭神的博客,回顾了下发现郭神已经把官方文档翻译完了,而且总结的非常好,不需要再重复造轮子了。这里直接贴上原文链接,感谢郭神贡献。/article/1562107.html

接下来我们使用几个工具分析下最常见的内存释放问题,内存泄漏:

MAT

MAT是我们常用的内存泄漏分析工具,使用它可以帮助我们找到代码中的内存泄漏,并且修复它。

下面的例子是Activity中,一个无法释放内存的例子。

public class SecondActivity extends Activity {
private List<String> list = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
for(int i=0;i<2000;i++){
list.add("test");
}
MemoryLeakClass memoryLeakClass = new MemoryLeakClass();
memoryLeakClass.start();
}

class MemoryLeakClass extends Thread {

@Override
public void run() {
while (true) {
Log.i("MemoryLeakClass", "run");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}


MemoryLeakClass是一个持续工作的线程,List是为了占用更多的内存方便演示内存无法释放问题。我们可以发现,因为Thread在Activity内部,所以Activity的引用无法释放,也就是Activity内存泄漏了。

我演示下mac平台下android studio环境的mat使用方式,其他环境也是大同小异。首先下载mat,链接地址: http://www.eclipse.org/mat/downloads.php 。选择mac平台下的下载后打开。

在android device monitor中选择自己应用的进程,点击update heap,选择heap后点击GC触发GC。



反复进入退出内存泄漏的界面,我们调用GC可以看到内存的情况。

Heap Size 堆的大小,当资源增加,当前堆的空余空间不够时,系统会增加堆的大小,若超过上限 (例如64M,视平台和具体机型而定)则会被杀掉

Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小

我们可以发现Allocated不断的增大,也就是内存无法被释放,如果我们的Activity中对象和资源更多,这个问题必定更加严重。

发现了内存泄漏,我们接下来要找到他,就要让mat派上用场了。

在android device monitor中点击导出选择保存位置后,打开命令行,输入命令 hprof-conv memoryleakdemo1.hprof converted-dump.hprof 后面的两个参数是文件的名字。



然后使用mat打开生成的converted-dump.hprof文件。点击创建histogram,然后选择list objects with incoming referenence,正则表达式匹配包名,然后右健 Path to GC Roots exclue all phantom/weak/soft etc. reference,就能看到最终的结果,activity实例自己this没有被释放。









可能引起内存泄漏的代码

单例模式

我们往往会习惯于使用activity作为listener,但是在activity销毁的时候没有注销监听。因为单例模式生命周期与application是相同的,所以造成activity被单例所持有,无法释放内存。

静态

如果我们类中的静态变量持有activity或者某些对象,因为静态变量生命周期是与类相同的就会造成内存泄漏。

无限循环的属性动画

这种情况比较容易被忽略,从3.0开始安卓支持属性动画,如果在activity销毁的时候不停止属性动画的循环,那么动画就还会一直循环下去,造成无法释放内存。

LeakCanary

推荐square推出的leakcanary,不得不佩服square总能给开发者们带来福音,有了这个库我们可以很方便的找到应用中的内存泄漏。使用方法超级简单。

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
}


public class ExampleApplication extends Application {

@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}


然后在清单文件中注册这个Application。这样就可以了,出现内存泄漏后,会弹出通知。我们继续使用上面的例子,进入SecondActivity然后再退出这个Activity。



甚至连泄露了42k都显示出来了,点击查看详情。



Logcat中也能看到日志



这里可以看到内存泄露的类名和具体泄露的详细信息。大大提升了查找内存泄漏问题的速度。在应用中Activity中的内存泄漏现在无处遁行了,如果觉得还不过瘾,我们再看看fragment中的内存泄漏怎么监控。

RefWatcher refWatcher = {...};

// We expect schrodingerCat to be gone soon (or not), let's watch it.
refWatcher.watch(schrodingerCat);


RefWatcher专门用来监控需要回收的对象。

在application中这样做

public class ExampleApplication extends Application {

public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}

private RefWatcher refWatcher;

@Override public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
}
}


fragment中的代码:

public abstract class BaseFragment extends Fragment {

@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}


有了这个神器,我们再也不会谈内存泄漏色变了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: