目录
垃圾回收机制
如何确定这是垃圾
- 垃圾需要回收,就需要确定对象是垃圾,但如何确定对象是垃圾呢?
- 有两种方法,引用计数法和可达性分析
引用计数法
- 方法解释:给对象添加一个引用计数器,每当一个地方引用它时,计数器加1,每当引用失效时,计数器减少1。当计数器的数值为0时,也就是对象无法被引用时,表明对象不可在使用
- 优点:实现简单,效率较高,大部分情况下不失为一个有效的方法。
- 缺点:对象之间的相互循环引用的问题
可达性分析
- 方法解释:通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
- 但是这种方法下来GC root就显得格外重要,什么对象可以作为GC root呢
- 虚拟机栈中本地变量表
- static成员
- 常量引用
- 本地方法栈中的变量
- 类加载器
- Thread
垃圾回收算法
- 如果已经确定是垃圾了,又如何回收呢,这个时候就需要垃圾回收算法,就像下图,蓝色代表可以继续存活的,灰色代表需要清理的,白色代表未使用的。
标记清楚算法
-
分为两个步骤:标记和清除
-
标记:找出内存中需要回收的对象,并且把它们标记出来
-
清除:清除掉被标记需要回收的对象,释放出对应的内存空间
-
缺点:
- 标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
- 标记和清除两个过程都比较耗时,效率不高
-
所以就出现了另一个算法,复制算法
标记复制算法
- 将内存划分为两块相等的区域,每次只使用其中一块,其中绿色表示划分出来的内存
- 当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次
清除掉。
- 优点:空间连续
- 缺点:空间利用率降低
标记整理算法
- 跟标记清除算法一样也是两个步骤:标记和整理
- 标记:标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活
的对象都向一端移动,然后直接清理掉端边界以外的内存。
- 整理:让所有存活的对象都向一端移动,清理掉边界意外的内存。
垃圾收集器
- 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
- 下图是历史以来的各个分代的垃圾收集器
Serial收集器
- 是一种单线程收集器,只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在进行垃圾收集的时候需要暂停其他线程。
- 优点:简单高效,拥有很高的单线程收集效率
- 缺点:收集过程需要暂停所有线程
- 算法:复制算法
- 适用范围:新生代
ParNew收集器
- 这个收集器理解为Serial收集器的多线程版本。
- 优点:在多CPU时,比Serial效率高。
- 缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
- 算法:复制算法
- 适用范围:新生代
- 应用:运行在Server模式下的虚拟机中首选的新生代收集器
Parallel Scavenge收集器
- Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集
器,看上去和ParNew一样,但是Parallel Scanvenge更关注系统的吞吐量。(吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集时间),若吞吐量越大,意味着垃圾收集的时间越短,则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。)
Serial Old收集器
- Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用"标记-整理算
法",运行过程和Serial收集器一样。
Parallel Old收集器
- Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和"标记-整理算法"进行垃圾
回收。吞吐量优先