Android内存相关知识分享:
Rain
一. Java垃圾回收机制:
1.引用计数法分析:
当对象被创建或被引用时,该对象的计数器值会加1,当其被释放时会减1,但当两个对象互相引用对方时,就会造成永久不被回收.(内存泄漏由此可见)
2.可达性分析:
GCRoot可达树节点为存或对象,其余的为死亡对象。换而言之,能够通过GCRoot根节点到达的对象都被视为存活对象.
一般讲下列四点作为GCRoot:
1.虚拟机栈(帧栈中的本地变量表)中引用的对象
2.方法区中静态属性应用的对象
3.方法区中常量应用的对象
4.本地方法栈中JNI应用的对象
Java垃圾回收的几种方式:
1.标记->清理
先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
因为在清理过程中,需要清理的内存地址不是连续的,所以清理后会造成很多的内存碎片
2.标记->整理
标记整理算法的“标记”过程和标记-清除算法一致,只是后面并不是直接对可回收对象进行整理,而是让所有存活的对象都向一段移动,然后直接清理掉端边界意外的内存。
由于会整理后统一进行清除,可见这样的回收方式不会造成太多的内存碎片
标记->整理->清除这种方式比较适用于,存活多,垃圾少的情况,这样的话,对于整体内存移动和回收来说是比较节省空间的。
3.停止->复制:
将可用的内存按容量划分为大小相等的两块(from,to),每次只是用其中一块(总有一块是空的【to区域】)。当这一苦熬的内存用完了,就将还存活着的对象复制到另外一块上面,然后把已使用过的内存空间一次清理完。
停止,复制就意味着在一段时间内,只能使用一部分内存空间,当使用的空间超出这一半内存空间大小后,就会频繁的触发GC.
此方法适用于,存活对象少,垃圾多的情况,这样就会复制少,清空多。
Java的分代机制:
Java的堆空间一般分为三部分来存储三类数据
新生代:
存放刚刚创建的对象,代码才开始运行会创建大量对象,这些对象会统一的存放在新生代,因为有很多局部变量对象等会很快的变成不可达状态,快速死去。
特点:存活对象少,垃圾多
老年代:
存活了一段时间的对象,从创建后就一直存活下来。
特点:垃圾少,存活对象多
永久代:
一些静态文件等,Java8中已经删除并将这块区域划分给了元空间。
[外链图片转存失败(img-4M37sl2f-1565406219643)(C:\Users\XGIMI\AppData\Local\Temp\1554277085476.png)]
特点:永久存活,不回收
各个分代的垃圾回收机制:
结合上面的三种垃圾回收机制,很容易得出:
名称 | 特点 | 回收机制 |
新生代 | 垃圾多,存活少 | 复制 |
老年代 | 存活多,垃圾少 | 标记-整理-清除 |
新生代垃圾回收流程:
新生代在每次GC后,都会有大量不可达垃圾死去,只有少量存活。
Java新生代内存划分区域为:
[外链图片转存失败(img-I0iLnCCM-1565406219645)(C:\Users\XGIMI\AppData\Local\Temp\1554278013970.png)]
1.Edent区对外提供最大可使用内存,当Edent区快要满的时候,则触发GC,把存活的对象移植到SA中,并清空Edent
2.当Edent再次快满的时候,此时会对Ed和SA区同时触发GC,活的对象存在到SB中,同时清空Ed和SA
3.一直重复该流程,当某个S区被填充满了后且任有对象未被复制到S区,或在某个对象移植反复的被放置到S区15次左右,则把这一部分放入Old区
4.当old区也被填满后,则会触发majorGC,对old代进行GC
老年代垃圾回收流程:
因为old区,本来就是存活较久的对象,所以每次GC只会有较少部分被回收,使用标记-整理-清除,通过少量的移动就可以实现内存清理而且避免了内存碎片化。
GC的触发类型:
GC_FOR_MALLOC:
表示在堆上分配对象内存不足时触发的GC
GC_CONCURRENT:
表示当我们应用程序的堆内存达到一定量时触发的GC
GC_EXPLICIT:
应用调用system.gc …VMRuntime.gc接口时,jvm收到SIGUSR信号时触发的GC
(kill -12 12345 or kill -SIGUSR2 12345 )
GC_BEFORE_OOM:
在发生OOM时触发的GC