JAVA虚拟机JVM-2.垃圾回收

判断对象是否“已死”

引用计数法

在对象中添加一个引用计数器,每当一个地方引用它时,计数器值+1,引用失效的时候,计数器值-1;当计数器值为0的时候说明对象“已死”。

这样做的好处就是简单快捷,且效率也很高,但是无法解决掉循环互相引用的问题。

可达性分析算法

通过一系列被成为“GC Roots”的根对象作为起始点集,从这些点开始,根据引用关系向下搜索,搜索过程所走的路径被称为“引用链”,如果某个对象到“GC Roots”间没有任何引用链,或者用图论的话来说就是从GC Roots到这个对象不可达时,这个对象就是不可能在被使用了。

引用概念

  • 强引用:new对象就是这种引用关系,这种引用永远不会被回收。
  • 软引用:描述一些还有用,但是非必须的对象。只被软引用关联着的对象,在系统发生内存溢出异常之前,会把这些对象列进回收范围中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
  • 弱引用:强度比软引用更弱的引用,只能生存到下次垃圾回收发生为止。
  • 虚引用:“幽灵引用”或者“幻影引用”,最弱的引用关系。

判断是否死亡

即使被算法判定为不可达对象,也不是非死不可,宣告一个对象真正死亡是需要经过两次标记的过程:可达性分析标记为第一次,随后进行筛选,筛选条件是此对象是否有必要执行finalize()方法。

如果对象没有覆盖finalize()方法,或者finalize()方法已经被调用过,那么这两种情况虚拟机认为没必要在执行finalize()方法。

如果对象吧内判定有必要执行finalize()方法,那么该对象将会被放置在一个名为F-Queue的队列中,并在稍后由一条由虚拟机自动建立的,低调度优先级的Finalizer线程去执行他们的finalize()方法。

回收方法区

  方法区的垃圾回收内容:废弃的常量以及不再使用的类型。

  判断废弃的条件:

  • 该类所有的实例都被回收。
  • 加载该类的加载器也被回收。
  • 该类对应的java.lang.Class对象没有被引用,无法在任何地方通过反射访问该类的方法。

垃圾回收算法

分代收集理论

建立在两个假说之上的理论:(第三条是为了解决新声代引用老年代的问题)

  1. 弱分代假说:绝大多数对象朝生夕灭
  2. 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡
  3. 跨代引用假说:跨代引用相对于同代引用来说仅占少数。

标记-清除算法

容易造成空间碎片化,如果遇见大对象需要连续空间,会触发Full GC

标记-复制算法

将内存分成三块区域,一块比较大的Eden区,和两块比较小的Survivor区域,比例默认为8:1:1。每次分配内存只使用一块Survivor区域和Eden区域,当发生垃圾收集时候,将Eden和Survivor中存活的对象一次性复制到另一块Survivor空间上,然后直接清掉Eden和已经使用过的Survivor空间。

如果其中一块Survivor空间不足于存放Minor GC之后存活的对象,就需要依赖其他区域进行分配担保(老年代担保)。

标记-整理算法

先标记之后,将空间进行整理,最后清除垃圾,这样保证了可用空间的连续性,优化了标记-清除算法中产生的空间碎片化问题。只是整理的时候对于系统的开销比较大,因为要对存活的对象进行移动,这样会产生“stop the word”的现象。

垃圾收集器

Serial收集器:

单线程收集器,新生代采用复制算法暂停所有用户线程(单线程),老年代采用标记-整理算法暂停所有用户线程(单线程)。

ParNew收集器:

多线程收集器,新生代采用复制算法暂停所有用户线程(多线程),老年代采用标记-整理算法暂停所有用户线程(单线程)。

Parallel Scavenge收集器

多线程收集器,但是和其他收集器关注点不同,关注的是吞吐量。

Serial Old收集器

是Serial收集器的老年代版本,同样是单线程收集器,使用标记清除算法。

Parallel Old收集器

是Parallel Scavenge收集器的老年代版本,支持多线程并发,基于标记整理算法实现。

CMS收集器

以获取最短回收停顿时间为目标的收集器。

  1. 初始标记。(单线程)
  2. 并发标记。(多线程)
  3. 重新标记。(多线程)
  4. 并发清除。(多线程)

G1收集器(Garbage First收集器)

开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。

猜你喜欢

转载自www.cnblogs.com/wangb0402/p/12622696.html
今日推荐