垃圾收集器和内存分配策略

垃圾收集器和内存分配策略

对象是否存活方法

  • 引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加一,引用失效时,计数器就减一。缺点是很难解决对象直接循环相互引用的问题,

  • 可达分析法。

通过一系列“GC Root” 的对象作为起点,从这些节点向下搜索,所经过的路径称为引用链,当对象到GC Root 没有任何引用链时,怎说明这个对象不可用。

引用

  • 强引用

强引用是程序代码中普遍存在的,比如Obje obj = new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

  • 软引用

用来描述一些还有用当时非必须的对象。对于软引用的关联对象,在系统将要内存溢出的时候,会把他们列进回收范围之中,进行第二次的回收,如果这次回收内存还是不够,则抛出内存溢出异常。

  • 弱引用

描述非必须对象的,但是强度比软引用还要弱一点。当垃圾回收器工作时候,不论内存是否够用,都会被回收

  • 虚引用

最弱的一种引用关系。对象设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

垃圾收集算法

  • 标记-清楚算法

首先标记需要回收的对象,标记完之后统一回收所有被标记的对象。缺点是效率低,还有回收之后会产生大量的内存碎片。

  • 复制算法

将可用内存划分为相同的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活的对象复制到另外一块上,然后把已经使用过的内存空间一次秦楚掉。缺点是将内存缩小为原来的一半。

  • 标记整理算法

前面步骤一样也是标记,但是清楚的时候,先让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。

  • 分代收集算法

将Java堆划分为新生代和老年代,新生代每次垃圾收集时候就发现有大批对象死去,只有少量存活。选用复制算法。老年代中对象存活率较高,使用标记清理或者标记整理算法。

垃圾收集器

  • G1垃圾收集器
  1. 并行与并发:通过多CPU,多核环境下的硬件优势,减少Stop The World的停顿时间。

  2. 分代收集:分代

  3. 空间整合:G1整体看,属于标记整理算法,局部来看是基于复制算法实现的。

  4. 可预测的停顿,G1可以建立可预测的停顿时间模型,能让使用者指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒。

G1对Java堆的内存布局和别的有很大的差别。它将整个Java堆划分为多个大小相等的独立区域(Region),后台维护一个Region的价值列表,有限回收价值大的Region。

内存分配和回收策略

  • 对象优先在Eden分配,当Eden区没有足够的空间进行分配时候,进行一次minor GC,

  • 大对象直接进入老年代。

  • 长期存活的对象将进入老年代,对象在Eden出生并经过一次Minor GC还存活,就会被Survivor容纳,年龄是1,年龄增加到一定的时候晋升到老年代(默认15岁)。可以设置年龄阈值。

  • 动态对象年龄判定。当在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,则年龄大于或者等于该年龄的对象就可以直接进入老年代。

  • 空间分配担保,在发生Minor GC之前,首先检查老年代中最大连续可用内存是否大于新生代中所有对象的总空间。如果成立可以则Minor GC是安全的,不成立的话,检查是否允许担保失败,如果允许,则会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。

上面的冒险是,因为新生代时使用的是复制收集算法,当出现大量对象在Minor GC后仍然存活的情况,就需要老年代进行担保,把Survivor区无法容纳的对象直接进入到老年代。