今天来说说android当中的GC机制(garbage collector)
参考网上大神的文章:
http://blog.csdn.net/luoshengyang/article/details/41822747
http://blog.csdn.net/suifeng3051/article/details/48292193
前言:
最近研究性能,其中最重要的一个性能指标就是内存泄露。身为一个良好的测试人员,除了能找到内存泄露的原因,反馈给开发,更重要的是理解内存泄露的原因以及内存相关的原理,作为一个测试人员技术的提高。(只讨论Dalvik虚拟机,JVM不算在内啦)
内存泄露的原因:
前一片文章已经说明了Android中开发过程中可能出现内存泄露的几个点,这里解释一下代码原因,一句话,就是在堆内存中的引用没有被释放掉,导致GC机制没有成功回收这部分new出来的对象,当重复操作某一个步骤的时候,对象就会不断被分配,导致的OOM,应用就拜拜了,避免内存泄露有机会再讨论了,今天学习以下GC的原理。
时机:
什么时候会触发GC呢?老罗的博客写了4个函数,给出了4个时机:
1.堆上分配对象时内存不足触发的GC
2.已分配内存达到一定数量触发GC
3.准备oom的时候系统最后挣扎
4.手动触发System.gc等
说白了就是堆内存不够了,要发生oom了或者开发手动GC了,就会触发GC机制,这个容易理解。
怎么样GC:
Dalvik虚拟机的回收方式是一个叫分代式的方式来回收的,GC根据代的不同执行不同的GC。为什么要引入这个呢?因为在执行清理的时候,系统要知道哪些对象是可以回收,就会从头遍历系统一遍,这样耗时耗力,严重影响性能,而内存中的部分堆对象是经常使用的,每次都全部寻找一遍未免会造成资源浪费,于是引入了分代式GC。
(图片来自http://jefferent.iteye.com/blog/1123677)
如图,分代式分为三个空间,年轻代,老年代和持久代。其中,年轻代又分为
一个Eden空间和两个Survivor空间。平常我们程序运行使用的是Eden空间,当这个空间占满了,会触发一次新生代的GC(Scavenge GC),这个时候会采用Mark-Sweep算法去标记并清除无用的对象(算法后面会提),那么垃圾对象就会被清除,存活对象会移动到Survivor任意一个空间,Eden空间被清除;然后,这个Survivor空间也满了,触发MS算法,清除垃圾对象,存活对象又会被移动到另一个Survivor空间,另一个Survivor空间又满了,那么可以判断这个对象是经常使用的,就移动到老年区了,移动到老年区的对象不会被普通的GC清除,android里面经常触发的是新生代GC机制,还有一个是Full GC,代表全部区域都触发,比较耗时耗CPU。Full GC一般是老年区或者持久区满了,那么就会触发了。Android通过这个方式,分代来进行GC,有效利用了系统资源进行分配,额,手动触发的GC都是Full GC了,所以不建议开发者一直手动触发这个方法,程序会卡的。
还有一个持久区,放的是一些静态变量,没啥可说的。
两个算法:
这里说两个算法,一个是年轻代的GC,还有一个是老年代的GC,分别是复制,标记-清除(Mark-Sweep)和标记-压缩(Mark-Compact)算法。
Coping:
根集扫描出存活对象,然后移动到另一个新的内存中去,年轻代GC就是用这种算法的。
(图片来自
http://blog.csdn.net/suifeng3051/article/details/48292193)
Mark-Sweep:
从根集开始扫描(根集,从当前线程的函数的引用开始扫描,然后标记,然后继续从当前线程继续扩展扫描,直到完结),当对象有引用,那么mark一下,完了之后就清除垃圾对象。
Mark-Compact:
和上面一个类似,但是多了个压缩的过程,我们知道Mark-Sweep之后清除垃圾对象,那么内存当中存在内存碎片,这个算法就是把碎片填满,老年代的Full GC采用这个。