android垃圾回收机制

目录

1."垃圾的判定"

1)JDK1.1 前的判定规则-引用计数算法

2)目前在用的可达性分析算法

2.回收算法

1)标记清除算法

2)复制算法

3)标记整理算法

4)分代回收算法

3.内存优化


垃圾内存不及时回收,则运行时的可用内存会越来越少,最终导致OOM(内存溢出)。而垃圾回收(GC),主要可从两个方面探讨:

1.怎么判定是"垃圾"?

2.怎么回收的?

1."垃圾的判定"

垃圾的判定主要是针对堆内存中的对象、数组等,当对象超出作用域,不被引用时,就可以被认定为是垃圾。

1)JDK1.1 前的判定规则-引用计数算法

当对象创建时候,都被绑定一个计数器,当对象被引用则计数+1,引用失效或超出作用域则计数-1,计数为0时即被Jvm判定为"垃圾"。效率虽高,但是A和B对象如果同时互相引用,计数都为1,即使A、B不再被使用,Jvm也不会检测到。而且,每次对象被引用时等操作还要触发计数,因此,额外开销在所难免,也就被弃用了。

2)目前在用的可达性分析算法

从"GC Root“对象开始,向下搜索,遇到可达的对象,就会形成一条链,将可达对象与"GC Root“关联。最终不与任何"GC Root“链相关的对象,都会被判定为“垃圾”。如下对象可认定为"GC Root"对象:

1.虚拟机栈(栈帧中的本地变量表)中引用的对象

2.方法区中静态属性引用的对象

3.方法区中常量引用的对象

4.本地方法栈中JNI引用的对象

5.活着的线程Thread

2.回收算法

1)标记清除算法

首先,会先标记所有需要回收的对象,接着将被标记的不可用的对象清除。优点是算法简单,只需要清理被标记的地址空间。缺点也比较明显,因为标记位置零散,很碎片化,导致被清除完后,可用的地址空间也零零散散。如果连续的可用内存空间少,那么遇到大的内存对象需分配内存时,就很难分配到适宜的连续内存空间。

2)复制算法

将内存空间平分为A、B两块,将A中存活的对象移动到B内存块中,再对A块所有对象进行清除。但是这个算法也有明显的缺点,那就是不管A区域或B区域有多少个存活对象,都需要将整块内存分成两个区域,意味着能够真正使用的内存变成了一半,造成内存的利用率不高。

3)标记整理算法

类似标记清理算法,将存活的内存对象标记,并平移到内存空间一侧,然后清理内存空间另侧数据。此算法解决了标记清除算法的导致的内存空间碎片化的问题。但是,缺点也有,就是多出了将存活内存平移的操作,会降低清除的效率。

4)分代回收算法

将对象进行代的划分,把不同生命周期的对象放在不同的代上使用不同的垃圾回收方式。主要代划分如下:

年轻代(例如:方法的局部变量等)

年老代(例如:缓存对象、单例对象等)

持久代(例如:加载过的类信息)

由上可知:堆大小=新生代+老年代

a、持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收相关的是年轻代和年老代。

b、年轻代又被分为3个部分:Enden区和两个Survivor区(From和to:这两个区是对称的,没先后关系)

内存大小比值为------>Enden区:From区:to区=8:1:1

新创建的对象会被放入Eden区,Eden区满了会执行小GC,会把存活对象转移(复制算法)到From或者to区,假设为From区。接着,Eden区接着存储新创建的对象,再次满时,Eden区和From区会执行小GC,将存活对象转移到to区。每次小GC后,存活对象都会在From或者to区,但是From或者to区也不会一直做苦力,多次转移相同的存活对象。因此,From和to区会有一个阈值,如果转移相同存活对象的次数超过阈值,这些对象就会被存放到老年代。

c、老年代默默存着年轻代转移过来的数据,当空间被用完时,会触发大GC(“标记-清除”或者“标记-整理”算法)。由于回收时间比较长,因此需要避免频繁触发大GC。

3.内存优化

1)对象无需引用,则置空为null,方便检测被垃圾内存

2)多用基本数据类型,少用它们的引用数据类型。例如Integer会比int所占大

3)少用static修饰变量,被修饰的全局性的变量是不会被GC回收的

4)字符串拼接要用StringBuffer(用append拼接)替代String,例如String str =str1+str2+str3+str4+str5,每多一个“+”,就会多创建一个对象。

5)bitmap、游标Cursor、IO或者文件流等不用时候,记得回收。尤其是图片的加载而占用内存较大,可以做图片质量或者物理大小的压缩。

6)避免在频繁绘制的ondraw方法中创建对象

7)Hashmap由于创建时候内存生成16位存储Entry节点的数组,一个Entry占空间32B。也就是说即使里面没有任何元素,也要分别一块内存空间给它。且存储数据每次大于当前容量最大值,Hashmap都会以X2容量的方式去扩容。所以在Android中,HashMap是比较费内存的。可以尝试android平台自带的SparseArray或者ArrayMap

详解:https://blog.csdn.net/u010687392/article/details/47809295

8)防止单例类长久持有不用对象的引用,导致对象无法回收,特别是传入上下文对象的单例类,可尝试传入ApplicationContext

9)非静态内部类导致内存泄露,比如Activity中创建的Handler,可以尝试弱引用去拿到外部的对象引用。

详解:https://blog.csdn.net/qq_37321098/article/details/81535449

10)广播的注销,页面Activity退出时未执行完的Thread的注销或者Timer还在执行定时任务的注销等等。

11)Activity结束时候,需cancel掉属性动画。

12)webview退出的销毁

    mWebViewContainer.removeView(mWebView);
    mWebView.stopLoading();
    mWebView.getSettings().setJavaScriptEnabled(false);
    mWebView.clearHistory();
    mWebView.removeAllViews();
    mWebView.destroy();

记得以前,webview如果内部播放视频,有背景声音,即使做了销毁,声音也还在,后来是页面销毁时,先reload重载webview去关闭声音,再销毁webview。有好方法,可以告诉下哦,谢谢!

你可能感兴趣的:(android面试)