android优化(2)--内存泄漏之工具使用

来源:互联网 发布:新手淘宝客哪里交流 编辑:程序博客网 时间:2024/06/06 00:28

最近找房子搬家,真是困难啊!卧槽,不知道什么时候北京的租房的价格都变成这样了!真是呆不下去了!

这里写图片描述

好了,闲话就到这了!进入正题!

android的快速发展已有几年了,市场也不再有前几年那样的火爆,并不是随便的写几个findViewById,写几个界面就可以要十几K工资的时期了,但是同样的中高级工程师还是很缺的,当初那些招聘快速开发而成的中型项目,问题还是很多的,当然如果你的客户群体是高端用户,手机的性能相对较好的那当我没说!但是就目前我遇到的,很大程度上都有问题,不是代码结构太乱,或者不怎么规范导致的后期维护有很大的困难,就是界面的卡顿,用户体验性很差!那。。。你既然接盘了,就不得不做一些改进优化!真是头大啊!!

先来解释内存泄漏是啥!

java的GC内存回收机制:某对象不再有任何的引用的时候才会进行回收。

内存不在GC掌控之内了。当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而就导致对象不能被回收。这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。

总之呢就是:由于其他对象的持有,被GC误解,没有自动回收,而实际已经无用的占着内存的对象!也就是那些占着厕所不拉屎的对象!

问题又来了,内存的分配策略是啥!

1.静态的
静态的存储区:内存在程序编译的时候就已经分配好,这块的内存在程序整个运行期间都一直存在。
它主要存放静态数据、全局的static数据和一些常量。
2.栈式的
在执行函数(方法)时,函数一些内部变量的存储都可以放在栈上面创建,函数执行结束的时候这些存储单元就会自动被释放掉。
栈内存包括分配的运算速度很快,因为内置在处理器的里面的。当然容量有限。
3.堆式的
通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。

区别:在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用

堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

举个例子吧

public class Sample {    int s1 = 0;    Sample mSample1 = new Sample();    public void method() {        int s2 = 1;        Sample mSample2 = new Sample();    }}Sample mSample3 = new Sample();

Sample 类的局部变量 s2 和引用变量 mSample2 都是存在于栈中,但 mSample2 指向的对象是存在于堆上的。 mSample3 指向的对象实体存放在堆上,包括这个对象的所有成员变量 s1 和 mSample1,而它自己存在于栈中。

我觉得不能再这么一个坑一个坑的填下去了!我们得进入今天的正题了,内存工具的使用了!内存泄露的面积有点广,各种花式泄露,如果比较感兴趣可以看看这篇总结,很到位!

https://github.com/francistao/LearningNotes/blob/master/Part1/Android/Android%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%80%BB%E7%BB%93.md

当然,我们在使用工具的前提我个人觉得应该是大概知道各种泄露的发生原因,不然全靠工具来分析的话,有点不靠谱!范围也有点大,工作量就他么的不谈了!

我们现在来写一个简单的单例模式的内存泄露样例,再使用分析工具分析一下。

代码如下:

这里写图片描述

我们再在activity中进行调用:

这里写图片描述

OK,看到这应该都明白,传入activity的上下文对象,导致TestClass对MainActivity的引用,所以当我们离开MainActivity的时候,导致MainActivity不能被GC回收!我们做一个简单的实验,不要在清单文件中配置Activity的属性,在它有旋转操作的时候销毁前一个activity再创建新的activity。我们再观察一下monitor中Memory的情况。

旋转之前,我们点击一下GC让它回收一下内存,最后内存的占有为14.17MB
这里写图片描述

多次旋转之后如下图:
这里写图片描述

内存的占有为16.27MB
我们再进行一下GC,多次GC后如图:

这里写图片描述

内存的占有为14.27MB,对比第一次的时候多了0.1MB,多的这部分是什么呢?

我们再点击GC旁边的那个按钮(Dump Java Heap),让它生成分析文件:

这里写图片描述

生成后的界面如下:
这里写图片描述

接着我们分析一下我们自己写的MainActivity:

这里写图片描述

发现左边的Instance的深度有两个,事先声明一下,我旋转手机至少有5次,为啥只有两个呢?

解释一下:现在内存中的两个Activity分别是第一次创建时的Activity和最后展示的activity,原因,第一个呢是因为TestClass的持有上下文对象,我们点击GC后没能被回收,那第二个到最后一个之前的呢,你看一下单例模式就明白了,它们未持有任何对象,而被GC无情的当垃圾回收掉了。
我们选择第0个MainActivity,那它的关联表就出来了:

这里写图片描述

查找引用了该对象的外部对象有哪些,
然后一个一个去猜,查找可能内存泄露的嫌疑犯,依据:看(读代码和猜)他们的生命周期是否一致(可以通过快照对比),如果生命周期一致了肯定不是元凶。

排除一些容易被回收的(软引用、虚引用、弱引用)

这里写图片描述

当我们的项目比较的复杂的时候,那关联树简直惨不忍睹啊,我们只能借助Mat工具分析了!

接着如下操作(导出标准的.hprof文件):

这里写图片描述

下载地址:

http://pan.baidu.com/s/1gfja0wF

OK,接着解压,运行Mat ,打开我们刚才生成的标准的hprof文件

这里写图片描述

接着呢,我们就可以开始利用Mat工具进行分析了!

文档在这:
http://pan.baidu.com/s/1nuYEMAP

相信大家看一下基本就都会用了!

好了,今天就到这了!有问题留言交流!

每天进步一点点,时间会让你成为巨人!加油!!

(内存泄露的参考链接已经在上面了!)

溜了溜了!

原创粉丝点击