垃圾收集器和内存分配

1.串行回收器

串行回收器是指使用单线程进行垃圾的回收。每次回收时,串行回收器只有一个工作线程,对于并行能力软弱的计算机来说,串行回收器的专注性和独占性往往有更好的性能表现。串行回收器可以在新生代和老年代使用,根据作用于不同的堆空间,分为新生代串行回收器和老年代串行回收器。

1.1新生代串行回收器

1)        串行回收器主要有两个特点:

        a)        它仅仅使用单线程进行垃圾回收。

        b)        它是独占的垃圾回收。

2)         在串行回收器运行时,应用程序中所有线程都停止工作,进行等待。这种现象称之为“Stop-The-World”。

3)        新生代串行处理使用复制算法,实现相对简单、逻辑处理特别高效、且没有线程切换的开销。


1.2 老年代串行回收器

老年代串行收集器使用的是标记算法。和新生代串行回收器一样,它也是一个串行的、独占的垃圾回收器。由于老年代垃圾回收通常会使用比新生代回收更长的时间,因此,在堆空间较大的应用程序中,一旦老年代串行收集器启动,应用程序可能会因此停顿较长时间。

2. 并行回收器

并行回收在串行回收器的基础上做了改进,它使用多个线程同时进行垃圾回收。

2.1 新生代ParNew回收器

   ParNew回收器是一个工作在新生代的垃圾收集器。它只是简单地将串行回收器多线程化,它的回收策略、算法以及参数和新生代串行回收器一样。ParNew回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行线程回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发并发能力弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程压力,它的实际表现很可能比串行回收器差。


2.2新生代ParallerGC回收器

新生代PadrallerGC回收器也是使用复制算法的收集器。从表面上看,它和ParNew回收一样,都是多线程、独占式的收集器。但是,ParallelGCl回收器有一个重要的特点:它非常关注系统的吞吐量。

注:ParallelGC回收器关注系统吞吐量。可以通过-XX:MaxGCPauseMillis和-XX:GCTimeRatio设置期望值的停顿时间和吞吐量大小。但是鱼和熊掌不可兼得,这两个参数是相互矛盾的,通常如果减少一次收集的最大停顿时间,就会同时减少系统吞吐量,增加系统吞吐量又可能会同时增加一次垃圾回收的最大停顿。

 

2.3老年代ParallelOldGC回收器

老年代ParallelOldGC回收器也是一种多线程并发的收集器。和新生代ParallelGC回收器一样,它也是一种关注吞吐量的收集器。ParalleleOldGC回收器使用标记压缩算法,它在JDK1.6中才可使用。


3. CMS回收器

与ParallelGC和ParallelOldGC不同,CMS回收主要关注于系统停顿时间。CMS是Concurrent Mark Sweep的缩写,意为并发标记清除,从名称上就可以得知,它使用是标记清除算法,同时它又是一个使用多线程并行回收的垃圾回收器。

3.1 CMS主要工步骤

1.        主要的步骤是:初始标记、并发标记、预处理、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而资源清理、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上说,CMS收集不是独占式的,它可以在应用程序过程中进行垃圾回收。

2.        根据标记清除算法,初始标记、并发标记和重新标记都是为了标记出需要回收的对象。并发清理是在标记完成后,正式回收垃圾对象。并发重置是指在垃圾圾回收完成后,重新初始化CMS数据结构和数据,为下一次垃圾回收做好准备。并发标记、并发清理和并发重置都是可以和应用程序线程一起执行的。

3.        预处理是并发的,除了为正式清理做准备和检查以外,预处理还会尝试控制一次停顿时间可能很长。为了避免这种情况,预处理时,会刻意等待新生代GC的发生,然后根据历史可能性数据预测下一次新生代GC可能发生的时间,然后在当前时间和预测时间中间时刻,进行重新标记。这样,从最大和程度上避免新生代GC和重新标记重合,尽可能减少一次停顿时间。

4.        并发是收集器和应用线程交替执行执行,并行是指应用程序停止,同时由多个线程一起执行GC。因此并行回收器不是并发的。因为并行回收器执行时,应用程序完全挂起,不存在交替执行的步骤。

5.        CMS回收是一个关注停顿的垃圾收集器。同时CMS回收在部分工作流程中,可以与用户程序同时运行,从而降低应用程序的停顿时间。

6.        CMS是一个基于标记清除算法的回收器。标记算法将造成大量内存碎片,离散有可用空间无法分配较大的对象。


4. G1回收器

G1回收器拥有独特的垃圾回收策略,这和之前提到的回收器截然不同。从分代上看,G1依然属于分代垃圾回收器,它会区分年轻代和老年代,依然有eden区和survivor区,但从堆的结构上看,它并不要求整个eden区、年轻代或者老年代都连续。它使用了分区算法。作为CMS的长期替代方案,G1同时使用了全新的分区算法,其特点如下:

(1)并行性:G1在回收期间,可以由多个GC线程同时工作,有效利用多核计算能力。

(2) 并发性:G1拥有与应用程序交替执行能力,部分工作可以和应用程序同时执行,因此一般来说,不会在整个回收期间完全阻塞应用程序。

(3) 分代GC:G1依然是一个分代收集器,但是和之前回收集不同,它同时兼顾年轻代和老年代。对比其他回收器,它们或者工作在年轻代,或者工作在老年代。因此,这里是一个很大的不同。

(4) 空间整理:G1在回收过程中,会进行适当的对象移动,不像CMS,只是简单地标记清除对象,在若干次GC后,CMS必须进行一次碎片整理。而G1不同,它每次回收都会有效地复制对象,减少空间碎片。

(5)可预见性:由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿也能得到较好的控制。

4.1 G1的内存划分和主要收集过程

     G1收集回收器将堆进行分区,划分为一个个的区域,每次收集的时候,只收集其中几个区域,以此来控制垃圾回收产生一次停顿时间。G1的收集过程可能有4个阶段:新生代GC、并发标记周期、混合收集、(如果需要)进行Full GC。

4.2 G1的新生代GC

    新生代GC的主要工作是回收eden区和survivor区。一旦eden区被占满,新生代就会启动。新生代GC收集前后的堆数据如图,其中E表示eden区,S表示survivor区,O表示老年代。可以看到,新生代GC只处理eden和survivor区,回收后,所有的eden区都应该被清空,而survivor区会被收集一部分数据,但是应该至少仍然存在一个survivor区,类比其他的新生代收集器,这一点似乎并没有太大变化。另一个重要的变化是老年代的区域增多,因为部分survivor区或者eden区的对象可能晋升到老年代。



4.3 G1的并发标记周期

1)        并发标记周期可以分为以下几步:

          a)        初始标记:标记从根节点直接可达的对象。这个阶段会伴随一次新生代GC,它是产生全局停顿的,应用程序在这个阶段必须停止执行。

          b)        根区域扫描:由于初始标记必然会伴随一次新生代GC,所以在初始化标记后,eden被清空,并且存活对象被移入survivor区。这个阶段,将扫描survivor区直接可达的老年代区域,并标记这些直接可达的对象。这个过程是可以和应用程序并发                  执行的。但是根区域扫描不能和新生代GC同时执行(因为根区域扫描依赖survivor区对象,而新生代GC会修改这个区域),因此如果恰巧在此时需要进行新生代GC,GC就需要等待根区域扫描结束后才能进行,如果发生这种情况,这次新生代                GC的时间就会延长。

          c)        并发标记:和CMS类似,并发标记将会扫描并查找整个堆的存活对象,并做好标记。这是一个并发的过程,并且这个过程可以被一次新生代GC打断。

          d)        重新标记:和CMS一样,重新标记也是会产生应用程序停顿的。由于在并发标记过程中,应用程序依然在运行,因此标记过程结果可能需要进行修正,所以在此对上一次标记结果进行补充。在G1中,这个过程使用SATB(Sbapshot-At-                               Begining)算法完成,即G1会在标记之初为存活对象创建一个快照,这个快照有助于加速重新标记的速度。

           e)        独占清理:这个阶段是会引起停顿的。它将计算各个区域的存活对象和GC回收比例并进行排序,识别可供混合回收的区域。在这个阶段,还会计算区域的存活对象和GC回收比例并进行排序,识别可供混合回收的区域。在这个阶段,还会更             新记忆集(Remebered Set)。该阶段给出了需要混合回收的区域并进行标记,在混合回收阶段,需要这些信息。

          f)         并发清理阶段:这里会识别并清理完全空闲的区域。它是并发的清理,不会引起停顿。

2)        下图显示了并发标记周期前后堆可能的情况。由于并发标记周期包含一次新生代GC故新生代被整理,但由于并发标记周期执行时,应用程序依然在进行,因此,并发标记周期结束后,又会有新的eden空间被使用。并发标记周期执行前后最大的不同是在该阶段后,系统增加了一些标记为G的区域。这些区域被标记,是因为它们内部的垃圾比例较高,因此希望在后续的混合GC中进行收集(注意在并发标记周期中并未正式收集这些区域)。这些将要被回收的区域会被G1记录一个称之为Collection Sets(回收集)的集合中。


并发回收阶段的整体工作流程,可以看到除了初始标记、重新标记和独占清理外,其他几个阶段都可以和应用程序并发执行。


4.4 混合回收

G1全称为GarbageFirst Garbage Collector,直译为垃圾优先的垃圾回收器,这里的垃圾优先(Garbage First)指的就是回收优先选取垃圾比例最高的区域。在这个阶段,既会执行正常的年轻代GC,又会选取一些被标记的老年代区域进行回收,它同时处理了新生代和老年代,如图。因为新生代GC的原因,eden区域必然被清空,此外,有两块标记位G的垃圾比例最高的区域被清理。被清理区的存活对象移动到其它区域,这样的好处是可以减少空间碎片。



4.5 在TLAB上分配对象

1.        TLAB的全称是ThreadLocal Allocation Buffer,即线程本地分配缓存。从名字可以看到,TLAB是一个线程专用的内存分配区域。

2.        考虑到对象分配几乎是Java最常用的操作,因此Java虚拟机就使用了TLAB这种线程专属的区间来避免多线程冲突,提高了对象分配效率。TLAB本身占用了eden区的空间。在TLAB启用的情况下,虚拟机会为每一个Java线程分配一块TLAB空间。

3.        如图展示了简要的对象分配流程。首先,如果运行栈上分配,系统会先进行栈上分配,没有开启栈上分配或者不符合条件则会进行TLAB分配,如果TLAB分配不成功,再尝试在堆上分配,如果满足了进入老年代的条件(PretenureSizeThreshold等参数),就在老年代分配,否则就在eden区分配对象,当然有必要,可能会进行一次新生代GC。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值