Hotspot垃圾回收

Hotspot VM 使用分代回收算法(Generational Collector)

  

GC堆的分代:

(1)Young Generation(年青代):大多数对象

       Eden区+Survivor_1区+Survivor_2区

(2)Old Generation(年长代):年青代的幸存对象(若干次回收后剩下的对象)+大对象

(3)Permanent Generation(持久代):元信息对象(ClassObject,Method...)

  

快速分配(Fast Allocation)    

bump-the-pointer:尾部指针(指向之前已分配对象的尾部)

     分配时比较(空间尾部和尾部指针的差值)剩余空间大小

   

线程局部分配缓冲(Thread-Local Allocation Buffers):

     每个线程有一个独立的分配缓冲(Eden区的一部分),再使用bump-the-pointer完成分配

  

  

1.串行回收(serial collector)
  使用串行收集器时,对年幼代和年长代的收集都采用串行、STW方式进行。也就是说收集任务同时只使用一个CPU,而且在收集任务执行期间,应用程序的执行被中止。
  (1)年青代

    首先把 eden中的活动对象复制到空闲的 survivor space中,即图中标为 To的区域,其中的一些大对象会被直接复制到老年代中。另一个 survivor space(标为 From)中仍然年轻的对象同样会被复制到 To 中,而其中的一些年老的对象会被复制到老年代中。注意,在向 To中复制对象的时候,如果 eden From中还有一些对象没有被复制的时候 To已经被填满了,那么 eden From中的这些对象不管它经历过几次新生代垃圾收集都会被直接复制到老年代中。edenFrom 中剩余的对象就成为了垃圾,它们所占用的内存将会被释放。

    经过一次新生代垃圾收集后,eden From都是空闲的,只有 To中含有活动的对象,此时,将交换两个 survivor space,即 From 被标识为 ToTo则为 From
  (2)年长代
  在串行垃圾收集器中,老年代和永生代使用标记清扫压缩(mark-sweep-compact)算法。在标记阶段,垃圾收集器标识出哪些对象是活动对象。清扫阶段则标识出垃圾对象。最后垃圾收集器执行平移压缩,把活动对象向老年代或永生代的一端平移,这样老年代或永生代的另一端就是一块连续的空闲内存

  

2.并行回收(parallel collector)
  目前,许多Java应用的运行平台大都包含很多物理内存和多个CPU。并行收集器,也被称作吞吐量收集器,被开发出来的主要目的就是为了充分利用CPU资源,而不是只让一个CPU去做垃圾收集而其他CPU却被闲置。
  (1)年青代
  和串行收集器相比,并行收集器采用了大致相同的年幼代收集算法,只是执行的是其并行版本而已。对年幼代的收集虽然仍基于拷贝技术、采用STW方式进行,但收集工作是并行展开的,使用了多个CPU,这就降低了垃圾收集开销,从而提高了应用程序的吞吐量。下图展示了并行收集器和串行收集器在执行年幼代收集时到底有何不同:

  (2)年长代
  和串行收集器一样,并行收集器对年长代的收集同样基于标记-清理-压缩算法,同样采用串行、STW方式进行。

  

3.并行紧缩回收(parallel compacting collector)

      (1)年青代

      同并行回收的年青代

      (2)年长代
      平行压缩垃圾收集器在老年代和永生代中并行地执行 stop-the-world式的垃圾收集,并进行平移压缩。这种垃圾收集器的垃圾收集过程分为三个阶段。在标记阶段,垃圾收集器首先会把每个代逻辑上划分为固定大小的区域 ( region ),这样应用程序中的强可达对象就分布于不同的垃圾收集线程中,垃圾收集线程可以并行地标记活动对象。一旦一个对象被标记为活动的,垃圾收集器会更新这个对象所在区域的数据结构,例如对象的大小及位置。

      汇总阶段的的操作是针对区域( region )的,而不是对象。由于之前进行的垃圾收集操作,每个代的左端活动对象的密度较高。从这种活动对象密度较高的区域中压缩回收空间所需的开销很大,但释放回收的内存空间却很有限,这样对这些区域进行压缩垃圾收集是很不划算的。因此在汇总阶段垃圾收集器从代的左端开始检查每个区域的活动对象的密度,直到发现这样一个区域,压缩回收这个区域以及这个区域右边的区域中的空间开销很小。当找到这样一个区域后,这个区域左边的区域会称为 dense prefix,而且不会有对象复制到这些区域中。对这个区域右边的区域进行压缩,并回收垃圾对象所占用的内存空间。汇总阶段会计算并保存压缩后的区域中对象的新地址。注意,当前汇总阶段的实现是串行的,当然汇总也可以实现为并行的,但相对于性能汇总阶段的并行不及标记压缩阶段来得重要。

      在压缩阶段,垃圾收集线程使用汇总阶段生成的数据标识出可以进行压缩填充的区域,这些线程并行地进行数据复制,从一个区域复制到另一个区域。这样就使堆的一端有大量的活动对象,而另一端则是一块大的空闲内存区域。 

     

4.并发标记清理回收(Concurrent Mark-Sweep Collector)CMS

      (1)年青代

      同并行回收的年青代

      (2)年长代

      老年代垃圾收集的大部分操作是与应用程序一起并发地执行的。

      CMS垃圾收集器进行老年代垃圾收集时首先会进行初始标记,在初始标记阶段 cms垃圾收集器会标识应用中的强可达对象,这个阶段会暂停应用的执行。接着 cms垃圾收集器进行并发标记,并发标记从初始标记阶段标识的活动对象开始,递归地遍历并标识这些对象中引用的强可达对象。由于并发标记时应用程序也在执行,运行中的应用程序可能会更新对象的引用,所以并发标记不能标识出内存中的全部活动对象。为了解决这个问题应用程序会被再次暂停,cms垃圾收集器再次访问在并发标记过程中引用被修改过的对象,这称为重新标记。为了提高重新标记的效率,多个线程会并行地执行重新标记。  

      重新标记执行完后,堆中所有的活动对象都被标识出来了,接下来的并发清扫阶段垃圾收集器会回收堆中的垃圾对象

      像重新标记这样的任务会增加垃圾收集的工作量,这也增加了垃圾收集的开销,所以降低暂停时间是通过增加垃圾收集的开销来获得的。

      cms 垃圾收集器是唯一一个进行紧缩的垃圾收集器,当 cms释放了垃圾对象占用的内存后,它不会把活动对象移动到老年代的一端。

这样做减少了 cms垃圾收集器进行垃圾收集花费的时间,但由于空闲的内存不是连续的,无法使用一个简单的指针来标识出下一个可以用来分配的空闲内存。cms垃圾收集器使用了一个空闲内存链表把空闲的内存链接在一起,每次分配内存的时候,垃圾收集器会在这个链表中查找出第一个能满足分配需要的空闲内存,所以从老年代中分配对象是十分昂贵的。这同样会影响到新生代垃圾收集,因为老年代中的大部分对象是在新生代垃圾收集的时候从新生代晋升为老年代的。

 

      cms垃圾收集器的另一个缺点是它需要的堆空间比其他的垃圾收集器要大。由于在标记阶段应用程序的执行并没有被暂停,应用程序执行过程中会继续申请内存,这样在垃圾收集的过程中老年代处于增长状态。另外,虽然垃圾收集器保证能够识别所有的活动对象,但有一些对象在标记过程中成为了垃圾,而这些对象在此次垃圾收集过程中并不会被回收,要等到下次垃圾收集才会被回收。这样的对象被称为 floating garbage 

 

      由于不执行压缩所以 cms 垃圾收集器很容易引起内存碎片。为了解决这个问题,cms会追踪常用的对象大小,预估内存需求,有时会分隔或合并空闲内存块。

 

      与其它垃圾收集器不同,cms垃圾收集器不会在老年代被填满的时候执行垃圾收集。相反,它会在老年代被填满之前尽早地执行垃圾收集,否则这就与串行或并行垃圾收集器一样会造成应用长时间地暂停。为了避免这种情况,cms会根据一些统计信息来决定执行垃圾收集的时机或者当老年代的使用达到某个伐值的时候也会启动 cms垃圾收集,通过命令行参数 -XX:CMSInitiatingOccupancyFraction = n设置初始伐值,其中 n是老年代大小的百分比,当来年代的使用达到这个伐值时 cms会启动,n默认为 68

 

      总体来看,与平行垃圾收集器相比,cms减少了执行老年代垃圾收集时应用暂停的时间,但却增加了新生代垃圾收集时应用暂停的时间、降低了吞吐量而且需要占用更大的堆空间

  

  

  

Hotspot JVM中的垃圾回收 

    

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值