转眼已是而立之年,随波逐流若干年并无太大成就,目前在某信集团安安稳稳做一个PM混日子
某日,突然顿悟,不愿再混吃等死,决定从新开始认识和学习陪伴自己多年的好伙伴“Android”
记录下重新渡劫的点点滴滴!
一、前言
一直都没有深入去了解GC回收机制,对于软件开发者来说还是有必要好好研究一下的,对APP的优化起到很好的帮助。
以下是参考过的文章,非常感谢各位作者!
Android内存优化5 了解java GC 垃圾回收机制3
Java gc(垃圾回收机制)小结,以及Android优化建议
二、GC机制
1. 什么是GC
GC是Garbage Collection [ˈɡɑːrbɪdʒ kəˈlekʃn]或Garbage Collector [ˈɡɑːrbɪdʒ kəˈlektər]的缩写,也叫垃圾回收器。为了避免出现内存问题,JAVA引入了自动内存管理机制,确保所有被引用的对象保留在内存中,把没有引用的对象内存进行回收。
2. 检测垃圾的方式
引用计数法:给一个对象添加引用计数器,每当有一个地方引用它,计数器就加1,引用失效就减1。
缺陷:如果有两个对象互相引用,除此之外没有其他任何对象引用他们,实际上这两个对象已经无法访问、但他们互相引用,计数不为0,所以无法回收。-
可达性分析算法 :通过一系列的被称为“gc roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到“gc roots”没有任何引用链相连时,则证明此对象是不可用的。
可以作为“gc roots”的对象:
(1)虚拟机栈(栈针中的局部变量表)中引用的对象
(2)方法区中类静态属性引用的对象。
(3)方法区中常量引用的对象
(4)本地方法栈中JNI引用的对象。
image.png
JVM在做垃圾回收的时候,会检查堆中的所有对象是否被这些“gc roots”的对象所引用,不能够被引用的对象就会被垃圾回收器回收。
3. 回收垃圾的方式
-
标记&清除(mark&sweep):
此算法分为两个阶段:标记和清除。标记所有需要回收的对象,之后统一回收。
缺陷:效率低,标记清除之后会产生大量碎片。效果图如下:
image -
复制(copying):
此算法把内存空间划分为两个相等的区域,每次只使用其中的一个区域。垃圾回收时,遍历当前使用区域的所有对象,将有引用的对象复制到另一个区域。此算法每次只处理有引用的对象,因此复制成本比较小,同时复制过去之后还能进行相应的内存整理,所以不会出现碎片问题。
缺陷:需要两倍的内存空间。效果图如下:
image -
标记&整理(mark&compact):
此算法结合了以上两个算法的优点,也分两个阶段,第一阶段是从根节点遍历所有对象标记所有能被引用的对象,第二阶段遍历整个堆中的对象,清除所有未被标记的对象,并把所有存活对象“压缩”到堆的其中一块,按顺序排放。避免了“标记--清除”的碎片问题,也没有“复制”的空间问题。效果图如下:
image -
分代收集算法:
image
内存分为三个部分:
(1)Young Generation [dʒenəˈreɪʃn]:新生代,分成Eden [ˈiːdn],Survivor [sərˈvaɪvər] 0 和 Survivor [sərˈvaɪvər] 1 三个部分。所有new 的对象都都在Eden区,当Eden区满时,可引用的对象将被复制到其中一个Survivor [sərˈvaɪvər]区,当此Survivor [sərˈvaɪvər]区的对象占用空间满时,此区可引用的对象又被复制到另一个Survivor [sərˈvaɪvər]区,反复多次后依旧可引用的对象将被复制到老年代。
(2)Old Generation [ˌdʒenəˈreɪʃn]:老年代,在这个区域存储的都是从新生代中转过来的长期可引用的对象;当可引用的对象在老年代存在时间达到一定程度时,将会被复制到持久代。
(3)Permanent Generation [ˈpɜːrmənənt ˌdʒenəˈreɪʃn]:持久代,用于存放静态的类和方法,以及JRE库的类和方法信息,持久代对垃圾回收没有显著的影响。
三、GC对APP的影响
GC在运行时,会停止其他所有线程,在GC开始运行和结束时均会发生,对象越多,停止的时间越长,会造成APP卡顿,因此需要尽量减少GC的运行时间,避免发生卡顿;为了达到这个目的,我们需要先了解一下GC的执行条件。
四、GC的触发条件
- GC所在线程的优先级最低,当程序空闲时,GC会被执行;
- 当内存不足的时候,JVM会强制执行GC,当一次运行过后,内存仍然不足,将再次运行GC,若还是不足,将发生内存泄漏(OOM)。
五、优化
- 少用静态对象,静态对象从新建的时候就存在于Permanent Generation [ˈpɜːrmənənt ˌdʒenəˈreɪʃn]持久代中,不会被GC回收;
- 对象使用完后,尽快释放,设置对象为null,空对象会加快GC的执行速度;
- 分散创建或删除对象时间,集中短时间内创建或删除大量对象,会导致内存紧张,JVM会增加执行GC的次数;
- 能用基本类型如int、long,就不用Integer、Long对象;基本类型变量占用的内存资源比引用类型变量占用的少得多;
- 尽量使用StringBuffer,而不用String来累加字符串;String每赋值一次,就会重新分配一次内存,String str = str1+str2+str3+str4+str5,每多一个“+”,就会多创建一个对象;
- 根据不同的场景使用不同的对象引用方式:
强引用:通过new出来的对象,即使内存不足,GC进行垃圾收集的时候也不会主动回收。
Object obj=newObject();
软引用:在内存不足的时候,GC进行垃圾收集的时候会被GC回收。
Object obj=newObject();SoftReference<Object> softReference = new SoftReference<>(obj);
弱引用:无论内存是否充足,GC进行垃圾收集的时候都会回收。
Object obj=newObject();WeakReference<Object> weakReference = new WeakReference<>(obj);
虚引用:和弱引用类似,主要区别在于虚引用必须和引用队列一起使用。
Object obj=newObject();ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);
引用队列:如果软引用和弱引用被GC回收,JVM就会把这个引用加到引用队列里,如果是虚引用,在回收前就会被加到引用队列里。