理解JVM之GC&内存分配
垃圾回收机制
- 对象存活判定算法
- 垃圾收集算法
- HotSpot算法实现和垃圾收集器
- 内存分配和回收策略
- 对象存活判定算法(JVM回收那些对象)
概念:四种引用类型
- 强引用:及时内存不足也不会被GC,不会随意回收强引用对象;即使抛出OOM(OutOfMemoryError),也不会随意回收强引用对象
- 软引用:只有内存不足时,会被GC;回收后内存不足会抛出OOM异常
- 弱引用:无论当前内存是否充足都会被GC
- 虚引用:任何时候都会别GC
a.引用计数算法:给对象添加一个引用计数器,每当有一个地方引用他的时候就加1,当有引用失效的时候就减1;任何时刻计数器为0的对象是不能被使用的
(但是因为他不能解决相互循环引用问题,所以没有被主流JVM所使用)
b.可达性分析法:以【GC ROOT】对象作为起始点,从这个节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC ROOT没人任何引用链时,则 这个对象是不可用的。
可以作为GC ROOT的对象
需要注意的是:在可达性分析法中被判定不可达对象未必真的就判死刑了,至少要经历两次标记过程,判断对象是否有必要执行finalize(),若判定有必要的话,还会在进行一次筛选,在finalize()中如果该对象与引用链中的任何一个对象建立关系,则它将被移除"即将回收"的集合 |
2.垃圾收集算法(介绍JVM怎么回收掉这些对象)
a.分代收集算法(是当前商业虚拟机都采用的一种算法)
- 新生代:大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可
- 老年代:对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。
b.复制算法
- 把可用内存按容量划分为相等的两块,每次只使用其中一块。当其中一块内存用尽后,把还存活着的对象复制到另外一块内存,在将这一块内存清理掉。
- 优点:每次都是对整个半区进行内存回收,无需考虑内存碎片等情况。只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效
- 缺点:每次可使用的内存缩小为原来的一半,内存使用率低
有研究表明新生代中的对象98%是朝生夕死的,因此没必要按照1:1来划分内存空间,而是分为一块较大的Eden空间和两块较小的Survivor空间, 在HotSpot虚拟机中默认比例为8:1:1。每次使用Eden和一块Survivor,回收时将这两块中存活着的对象一次性地复制到另外一块Survivor上,再做清理。可见只有10%的内存会被“浪费”,倘若Survivor空间不足还需要依赖其他内存(老年代)进行分配担保。 |
c.标记清除法:首先标记需要被回收的对象,然后统一清除这些对象
缺点:标记、清除效率不高;空间碎片太多,会产生大量不连续的空间碎片,可能会导致在后面需要分配较大对象时,因为无法找到连续较大的空间而提前 触发另一次GC,影响性能。
d.标记-整理算法:首先『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
优点:即没有浪费50%的空间,又不存在空间碎片问题,性价比较高。
一般情况下,老年代会选择标记-整理算法。