《HotSpot实战》笔记4垃圾收集

1.堆与GC


1.1垃圾收集


       负责垃圾收集的程序模块,称为垃圾收集器。实现一款垃圾收集器,首先需要明确他的主要任务:

  • 确保仍被引用的对象在内存中保持存在。
  • 回收无任何引用的对象所占用的内存空间。

       在设计垃圾收集器时,会有一些策略值得商榷:

  • GC工作线程:串行还是并行
  • GC工作线程与应用线程的关系:并发还是暂停应用程序?
  • 基本收集算法:压缩、非压缩还是拷贝?

      垃圾收集器的性能指标主要包括以下几项。

  • 吞吐量:应用程序运行时间/(应用程序运行时间+垃圾收集时间)。即没有花在垃圾收集的时间占总时间的比例。
  • 垃圾收集开销:与吞吐量相对,这表示垃圾收集耗用时间占总时间的比例。
  • 暂停时间:在垃圾收集操作发生时,应用程序被停止执行的时间。
  • 收集频率:相对于应用程序的执行,垃圾收集操作发生的频率。
  • 堆空间:堆空间所占内存的大小
  • 及时性:一个对象由称为垃圾到被回收所经历的时间。

1.1.1收集算法

      在现代虚拟机中,GC常用的基本收集算法主要有如下3中类型。

      (1)标记-清除(Mark-Sweep),该算法可分为两个阶段

  • 标记阶段:标记出所有可回收的对象。
  • 清除阶段:回收所有已标记的对象,释放这部分空间。

      (2)复制算法(Copying)

  • 划分区域:将内存区域按比例划分为1个Eden区作为分配对象的“主战场”和2个幸存区(即suvivor空间,划分为2个等比例的from区和to区)。
  • 复制:收集时,打扫战场,将Eden区中仍存活的对象复制到某一幸存区中。
  • 清除:由于上一阶段已确保仍存活的对象已被妥善安置,现在可以清理“战场”了,释放Eden区和另一块幸存区。
  • 晋升:若在复制阶段,一块幸存区接纳不了所有的幸存对象,则直接晋升到老年代。

      (3)标记-压缩算法(Mark-Compack),该算法分为两个阶段。

  • 标记阶段:标记出所有可回收的对象。
  • 压缩阶段:将标记阶段的对象移到空间的一端,释放剩余的空间。

1.2分代收集


      分代收集是指在内存空间中划分不同的区域,在各自区域中分别存储不同年龄的对象。每个区域可根据自身的特点灵活采取适合自身的收集策略。


      垃圾收集类型分为两种。

  • Minor Collection:对新生代进行收集。
  • Full Collection:除了对新生代进行收集外,也对老年代或永久代进行收集,又称为Major Collection。Full Collection对所有分代都进行了收集:首先,按照新生代配置的收集算法对新生代进行收集;接着,使用老年代收集算法对老年代和永久代进行收集。


1.2.1分代模型



1.3快速分配

       通常情况下,系统有大量连续的内存块可以用来分配对象,这种情况下若使用碰撞指针(bump-the-pointer)算法来进行对象内存空间分配的话,效率是可观的。

        bump-the-pointer是一种线性分配技术。以HotSpot为例,垃圾收集器在完成GC后,内存空间已使用和未使用的内存块是相对独立并且地址连续的。收集器在内部维护了一个记录上一次分配对象的末尾指针,当需要分配新的对象时,检查剩余空间能否满足新对象的分配,如果满足的话,则只需要移动指针就可以完成空间分配,所以效率很高。

        对于多线程应用,分配操作需要保证线程安全,如果通过全局锁的方式来保证线程安排的话,内存分配将会成为性能瓶颈。所以HotSpot采用的是线程局部分配缓存技术(即Thread-Local Allocation Buffers,简称TLABs)。每个线程都会有自己的TLAB,位于Eden区中的一小块空间。因为每个TLAB是仅对一个线程可见的,所以分配操作可以使用bump-the-pointer技术快速完成,而不必使用任何锁机制;只有当线程将TLAB填满并且需要获取一个新的TLAB时,同步才是必须的。


1.4栈上分配和逸出分析


        在栈中分配的基本思路是这样的:分析局部变量的作用域仅限于方法内部,则JVM直接在栈帧内分配对象空间,避免在堆中分配。

        这个分析的过程称为逸出分析,而栈帧内分配对象的方式称为栈上分配。这样做的目的是:减少新生代的收集次数,间接提供JVM性能。虚拟机是允许对逸出分析开关进行配置的,从Sun Java 6u23以后,HotSpot默认开启逸出分析。


2垃圾收集器


1、串行收集器:Serial

        串行收集器采用单线程方式进行收集,且在GC线程工作时,系统不允许应用程序打扰。此时,应用程序进入暂停状态。具有代表性的串行收集器有以下两种:

  • Serial收集器,作用于新生代,基于“复制”算法;
  • Serial Old收集器,作用于老年代,基于“标记——整理”算法;

2、并行收集器:ParNew

        并行收集器充分利用了多处理器的优势,采用了多个GC线程并行收集。在HotSpot中具有代表性的并行收集器有以下两种:

  • ParNew收集器:作用于新生代,基于“复制”算法;
  • Parallel Old收集器,作用于老年代,基于“标记——整理”算法;

3、吞吐量优先收集器:Parallel

        在ParNew基础上演化而来的Parallel Scanvenge收集器被誉为“吞吐量优先”收集器,仅作用于新生代。

4、堆的类型



        HotSpot提供了一些VM选项,可以用来选择堆的类型。事实上,这是由配置的收集器决定的。系统将按照如下顺序选择堆的类型。

        若开启VM选择UseParallelGC,系统将自动选择堆类型为ParallelScavengeHeap。

        若开启VM选项为UseG1GC,系统将自动选择堆类型为G1CollectedHeap,而收集策略只能选择G2专用的G1CollectorPolicy_BestRegionsFirst。

        若上述2个选择都没有配置,则将选择堆类型为GenCollectedHeap。而对于收集策略,还可以细分,例如:

  • 默认配置ConcurrentMarkSweepPolicy,老年代的收集使用“标记——清除”算法;
  • 若配置UseSerialGC,将选择MaskSweepPolicy;
  • 若配置UseConcMarkSweepGC,将选择ConcurrentMarkSweepPolicy;若开启了自适应策略选项UseAdaptiveSizePolicy,则选择ASConcurrentMarkSweepPolicy作为收集策略。

5、收集策略

        收集策略指的是在各个分代中应用的垃圾收集算法组合。我们先看一下在各个分代中可以配置的收集算法类型。

        (1)在新生代和老年代中。

  • ASParNew:在ParNew的基础上应用了自适应调节策略(UseAdaptiveSizePolicy)。
  • ASConcurrentMarkSweep:在ConcurrentMarkSweep的基础上应用了自适应调节策略。
  • DefNew:默认新生代收集器。
  • ParNew:并行收集新生代。
  • MarkSweepCompact:基于“标记-清除-压缩”算法。
  • ConcurrentMarkSweep:基于CMS收集器。

        (2)在永久代中。

  • MarkSweepCompact:基于“标记-清除-压缩”算法。
  • MarkSweep:基于“标记-清除”算法。
  • ConcurrentMarkSweep:基于CMS收集器。



2.1CMS收集器


       CMS收集器包含的主要模块:


        CMSAdaptiveSizePolicy模块:实现CMS自适应调节策略。

        CMSCollectorPolicy模块:实现CMS收集策略。

        ConcurrentMarkSweepGeneration模块:实现CMS分代。包括CMS的核心组件,如CMSCollector、CMSMarkStack、SweepClosure、ConcurrentMarkSweepGeneration、MarkFromDirtyCardsClosure、SurvivorSpacePrecleanClosure、CMSBitMap等。

        CMSPermGen模块:在CMS空间中实现PermGen,永久代也由CMS收集器收集。

        CMSGeneration模块:实现CMS分代。

        ConcurrentMarkSweepThread模块:实现CMS线程。

        VMCMSOperation模块:定义了在CMS内部执行的操作,如VM_CMS_Initial_Mark、VM_CMS_Final_Mark等。


2.1.1.如何找到垃圾——可达性分析


        CMS收集器工作时,GC工作线程与用户线程可以并发执行,以达到降低收集停顿时间的目的。对于交互响应速度敏感的应用程序,非常适合这种垃圾收集器。CMS仅作用于老年代的收集。



        为了确保可达性分析得整个阶段中对象的引用关系保持固定,JVM需要暂停其他工作。JVM暂停的时候,需要选择一个时机,由于JVM系统运行期间的复杂性,不可能做到随时暂停,因此引入了安全点(safepoint)概念:程序只有在运行到安全点的时候,才准暂停下来。HotSpot采用主动中断的方式,让执行现场在运行时轮询是否需要暂停的标识,若需要暂停则中断挂起。

        为支持分阶段实施并发“标记-清除”收集算法,HotSpot内部定义了多个CMS状态,其状态机如下图。


        CMS完整的收集过程如下。

  • 初始标记
  • 并发标记
  • 并发预清理
  • 重新标记
  • 并发清理
  • 并发重置



2.2G1收集器


        G1重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行。


1、何时使用G1

        官方建议,如果发现符合如下特征,可以考虑更换成G1收集器以追求更佳性能:

  • 实时数据占用了超过半数的堆空间。
  • 对象分配率或“晋升”的速度变化明显。
  • 期望消耗耗时较长的GC或停顿(超过0.5~1秒)

2、核心思路:Region

        G1将堆分成许多相同大小的区域单元。


        G1收集过程如下:

        (1)初始标记:STW。G1将这个过程伴随在一次普通的新生代GC中完成。该阶段标记的是幸存区Regions(Root Regions)。当然,该区域仍有可能引用老年代的对象。

        (2)根区域扫描:扫描幸存区中引用老年代的Regions。该阶段与应用程序并发进行。这一过程必须能够在新生代GC发生前完成。

        (3)并发标记:找出全堆中存活的对象。该阶段与应用程序并发进行。这一过程允许被新生代GC打断。

        (4)重新标记:STW,完成堆中存活对象的标记。冲击标记基于SATB(snapshot-at-the-beginning)算法,比CMS收集器算法快很多。

        (5)清理:包括3个阶段:首先,计算活跃对象并完全释放自由Regions(STW);然后,处理Remembered Sets(STW);最后,重置空闲Regions并将它们放回空闲列表(并发)。

        (6)复制:STW。将存活对象疏散或复制至新的未使用区域内。



《HotSpot实战》系列笔记共计4篇:

  1. 启动
  2. 类与对象
  3. 运行时数据区
  4. 垃圾收集

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值