目录
1."垃圾的判定"
1)JDK1.1 前的判定规则-引用计数算法
2)目前在用的可达性分析算法
2.回收算法
1)标记清除算法
2)复制算法
3)标记整理算法
4)分代回收算法
3.内存优化
垃圾内存不及时回收,则运行时的可用内存会越来越少,最终导致OOM(内存溢出)。而垃圾回收(GC),主要可从两个方面探讨:
1.怎么判定是"垃圾"?
2.怎么回收的?
垃圾的判定主要是针对堆内存中的对象、数组等,当对象超出作用域,不被引用时,就可以被认定为是垃圾。
当对象创建时候,都被绑定一个计数器,当对象被引用则计数+1,引用失效或超出作用域则计数-1,计数为0时即被Jvm判定为"垃圾"。效率虽高,但是A和B对象如果同时互相引用,计数都为1,即使A、B不再被使用,Jvm也不会检测到。而且,每次对象被引用时等操作还要触发计数,因此,额外开销在所难免,也就被弃用了。
从"GC Root“对象开始,向下搜索,遇到可达的对象,就会形成一条链,将可达对象与"GC Root“关联。最终不与任何"GC Root“链相关的对象,都会被判定为“垃圾”。如下对象可认定为"GC Root"对象:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.方法区中静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI引用的对象
5.活着的线程Thread
首先,会先标记所有需要回收的对象,接着将被标记的不可用的对象清除。优点是算法简单,只需要清理被标记的地址空间。缺点也比较明显,因为标记位置零散,很碎片化,导致被清除完后,可用的地址空间也零零散散。如果连续的可用内存空间少,那么遇到大的内存对象需分配内存时,就很难分配到适宜的连续内存空间。
将内存空间平分为A、B两块,将A中存活的对象移动到B内存块中,再对A块所有对象进行清除。但是这个算法也有明显的缺点,那就是不管A区域或B区域有多少个存活对象,都需要将整块内存分成两个区域,意味着能够真正使用的内存变成了一半,造成内存的利用率不高。
类似标记清理算法,将存活的内存对象标记,并平移到内存空间一侧,然后清理内存空间另侧数据。此算法解决了标记清除算法的导致的内存空间碎片化的问题。但是,缺点也有,就是多出了将存活内存平移的操作,会降低清除的效率。
将对象进行代的划分,把不同生命周期的对象放在不同的代上使用不同的垃圾回收方式。主要代划分如下:
年轻代(例如:方法的局部变量等)
年老代(例如:缓存对象、单例对象等)
持久代(例如:加载过的类信息)
由上可知:堆大小=新生代+老年代
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。
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。有好方法,可以告诉下哦,谢谢!