java垃圾收集器与内存分配策略(2)

 

java垃圾收集器与内存分配策略

 

1. 根搜索算法(GC Roots Tracing)

java语言中判定一个对象是否存活,用的是根搜索算法。

算法思路:从一系列名为“GC Roots”的对象作为起始点,开始向下搜索,搜索经过的路线称为引用链。当一个对象到GC Roots没有任何应用链相连,则证明此对象是不可用的。

可以作为GC Roots的对象包括:

(1)虚拟机栈(栈帧中的本地变量表)中的引用的对象。

(2)方法区中的类静态属性引用的对象。

(3)方法区中常量引用对象。

(4)本地方法栈中JNI(native方法)的引用对象。

对象不可用并不是代表一个对象已经死亡,可以进行回收,在这之前至少要经历两次标记过程:如果对象被宣告为不可用,那它将会被第一次标记并进行第一次筛选,筛选条件是此对象是否有必要执行finalize()方法。当对象没有覆盖该方法,或者该方法已经被虚拟机执行,则这两种情况都被视为“没有必要执行”。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将被放置在一个名为F-Queue的队列中,并在稍后由一条由虚拟机自动创建的、低优先级的Finalizer线程去执行,但是虚拟机不会阻塞在该方法上,这是为了避免由于finalize()方法执行时间过长或者死循环造成的内存回收系统崩溃。finalize()是对象逃逸的最后一次机会。如果对象在finalize()方法成功的将自己和引用链连接上,那么在F-Queue上的第二次标记时,它将被移出即将回收的集合。还有一点要注意的是finalize()方法对于一个对象而言只会执行一次。

 

2. 引用的类型

jdk1.2以后java对引用的概念进行了扩充:

(1)强引用(Strong Reference):在程序代码中普遍存在的。类似“Object obj = new Object()”,这类引用只要存在垃圾收集器永远不会回收。

(2)软引用(Soft Reference):一些还有用,但并非必须的对象。系统将在发生内存溢出异常之前,将这些对象列进回收范围之中并进行第二次回收。如果回收后还没有足够内存,才抛出异常。

(3)弱引用(Weak Reference):也是描述非必须对象,被弱引用关联的对象只能生存到下一次垃圾收集之前。

(4)虚引用(Phabntom Reference):最弱的引用关系。为一个对象设置虚引用的唯一目的就是希望能在这个对象被回收时收到一个通知。

 

 3.垃圾收集算法

3.1 标记-清除算法(Mark-Sweep)

首先标记出要回收的对象,在标记完成后统一回收掉被标记的对象。

缺点:效率问题,标记和清除效率都不高。导致产生大量不连续的内存碎片。

3.2 复制算法

将空间容量等量的划分成两块,每次使用其中一块。当当前块用完时,将存活对象复制到另外一块,然后将前面一块清理掉。

优点:实现简单、运行高效。

缺点:内存缩小了一半,空间代价高昂。

现在的商业虚拟机都采用这种方法来回收新生代。新生代的对象一般都是朝生夕死,所以不需要1:1的划分空间。而是将分为较大的一块为Eden,两块较小为Survivor空间,每次使用一块Survivor,回收时将Eden和Survivor中存活对象复制到另外一块Survivor空间。HotSpot默认Eden:Survivor=8:1。但是也有可能会出现Survivor大小不足以满足一次复制的需求,这就需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion),一旦出现这种Survivor空间不够的情况,这些对象将直接进入老年代。

3.3 标记-整理算法

复制算法在对象存活率较高时就要执行较多的复制操作,效率就变低。如果Survivor空间不足还需要额外的空间分配担保。所以根据老年代的特点,运用“标记-整理”算法。标记过程和前面提到的标记算法一致,后面将存活对象都移动到空间的一段,然后清理另一端。

4.垃圾收集器

(1)Serial收集器:用户线程将暂停,GC为单个线程。

(2)ParNew收集器:Serial收集器的多线程版本。用户线程将暂停,在新生代收集时GC为多个线程。

(3)Parallel Scavenge收集器:并行的多线程收集器。其他收集器关注缩短收集时用户线程暂停时间,而此收集器的目标是到达一个可控的吞吐量(Throughput),就是cpu用于用户的时间与CPU总消耗时间的比值。

(4)Serial Old收集器:Serial收集器的老年代版本。单线程。

(5)Parallel Old收集器:Parallel Scavenge收集器的老年代版本。多线程。

(6)CMS(concurrent Mark Sweep)收集器:最短回收时间为目标的收集器。垃圾收集过程分为4个过程:

  • 初始标记:标记GC Roots能直接关联到的对象,速度快。单线程。不可以与用户线程一起工作。
  • 并发标记:进行GC Roots Tracing的过程。单线程。可以与用户线程一起工作。
  • 重新标记:修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记稍长,但是远比并发标记时间段。多线程。不可以与用户线程一起工作。
  • 并发清除:时间最长,收集器线程可以与用户线程一起工作。单线程。
并发标记和并发清除耗时最长。Serial和ParNew能够和它配合使用。
(7)G1收集器:不牺牲吞吐量的前提下完成低停顿的的内存回收。G!将整个堆划分为多个大小固定的区域,根据垃圾堆积程度,在后台维护一个优先列表,每次在允许的收集时间,优先回收垃圾最多的区域。

5.内存分配与回收策略

(1)在新生代进行Minor GC。发生频繁,回收速度也比较快。老年代进行Full GC,经常会伴随一次Minor GC,也叫Major GC一般比Minor GC慢10倍。

(2)多数情况下,对象直接在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。当Survivor区空间不足时,Minor GC将对象存入老年代。

(3)大对象直接进入老年代。虚拟机提供参数-XX:PretenureSizeThreshold参数设定,如果大于这个参数,怎对象直接进入老年代。
(4)长期存活的对象将进入老年代。如果对象在Eden出生并经过第一次Minor GC后仍然能存活,并被Survivor容纳,将被移动到Survivor区,并将年龄增加1,对象每熬过一次Minor GC年龄就加1。当年龄增加到一定程度时,将被晋升到老年代。这个阀值可以通过设置参数-XX:MaxTenuringThreshold来设置。但是也存在动态的年龄判定,如果Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象直接进入老年代,无须等到满足该参数。
(5)空间分配担保:发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则直接进行一次Full GC。如果小于,则查看HandlePromotionFailure设置是否允许担保失败。如果允许,只进行Minor GC。如果不允许,则也要改为一次Full GC。前面提到,当Survivor空间不足时,就要在老年代中分配担保。这样也会经历刚刚分析的过程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值