前言
今天初步来了解一下有关于垃圾回收的基本入门知识,听大佬们说这些都是皮毛,可是还是花了挺多时间来理解这几个知识点
-
具体如何来判断对象是否存活?
-
强、软、弱、虚引用到底是些啥?
-
垃圾回收算法有哪几种?
-
分代模型又是什么?
1、判断对象是否存活的两种方式
引用计数法
在对象中添加一个引用计数器,每当有一个地方引用就加1,有一个地方引用失效就减1,任何时刻计数器为0的对象都是不可能再使用的。
优点:实现简单,判定效率高
问题:很难解决对象之间相互循环引用的问题
可达性分析算法
通过“GC Roots”对象为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象没有任何引用链与其相连时,则该对象不可用,就会判定为可回收对象。
2、强、软、弱、虚四种引用
强引用
强引用是在程序代码中普遍存在的,类似于Object obj = new Object()这类引用,只要强引用还存在,垃圾回收期永远不会回收掉被引用的对象
软引用
软引用是用来描述一些还有用但是非必需的对象,对于软引用关联着的对象,在系统将要发生内存溢出之前,将会把这些对象列进回收范围之内进行二次回收,如果这次回收还是没有获得足够的内存,才会抛出内存溢出异常
弱引用
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生之前,当垃圾回收器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象
虚引用
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生之前,当垃圾回收器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象
3、垃圾回收算法
标记清除
标记清除算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,在标记完成之后统一回收所有被标记的对象。
适用场景:
- 对象存活比较多的时候(老年代)
缺点:
-
产生大量不连续的空间碎片,
-
提前触发GC,空间碎片太多会导致程序运行过程中需要分配大对象时候,无法找到足够的连续内存而提前触发GC
-
需要扫描两次,标记存活对象和清除存活对象
复制算法
复制算法将可用内存按照容量划分为大小相等的两部分,每次使用一块。当这一块内存使用完了,就将还存活的对象复制到另一块上,然后把已使用过的内存空间一次性进行清理。
对象存活很多时候,复制算法就需要大量的复制操作,效率就会降低。还有就是不想浪费50%的空间,在应对对象100%存活的极端情况时就需要额外的空间来进行分配担保,因此,老年代中一般不采用复制算法
适用场景:
- 存活对象少 比较高效
- 适合年轻代
缺点:
- 需要内存空间
- 需要复制移动对象
标记整理
标记过程与“标记清除”算法一致,然后让所有存活的对象都向一端进行移动,最后直接清理掉除此存活对象区域之外的内存。
优点:
- 不会产生碎片,方便对象分配
- 不会产生内存减半
缺点:
- 需要扫描两次
- 效率低
4、分代回收
现在的垃圾回收器,都会在物理上或者逻辑上,把这两类对象进行区分。我们把死的快的对象所占的区域,叫作年轻代(Young generation)。把其他活的长的对象所占的区域,叫作老年代(Old generation)。在年轻代中选用复制算法,老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记清理”或者“标记整理”算法。
具体的分代回收和各类垃圾回收器在下一篇文章中再总结