关于JVM内存回收算法的补充

 

1.       再说JVM的分代回收策略

 

HotSpot的垃圾回收机制采用分代回收,堆分为年轻代和老年代,非堆就是持久带。面对不同的代,采用不同的回收策略。

 

而年轻代又可以分为1Eden2Survivor。对于Eden,大多数对象都是先在此区域开辟空间,存储年轻对象的实例数据。Eden相对于Old带,空间是比较小的。所以对象数据不断地在此开辟空间,Eden不够了,年轻代发生了回收。放置(稍后会说怎么放置)到S1或者S2S1或者S2不够放了,直接放到Old带。根据年轻代的特点,空间小,发生回收事件频率较高,那么就采用标记-复制的算法将Eden中的对象实例数据克隆到S1或者S2或者直接克隆到Old带。标记-复制算法优点是:快速、节省内存碎片。缺点是:内存消耗的空间需要变为原来的50%,另一块空间作为复制的目标。

 

老年代因为空间比较大,存储的对象又是比较长寿的对象,所以采用标记-整理或者也称作标记-压缩算法。这样不必开辟另外50%的空间用于复制目标,也不用担心这个较为辽阔的内存空间产生占用碎片的问题。缺点就是又标记、又压缩的。对于Old带比较费时间。

 

持久带虽然资料上没有明说,但是根据持久带的作用和特点以及触发该区回收的情景可以推算,持久带采用的回收算法应该是标记-整理/压缩算法。

 

JVM内存回收只要是针对这3个区域来说的。像NIO的直接文件内存读取,使用的是直接内存,只有出发了FullGC方能回收该区域!JNI调用本地库,本地代码所消耗的内存需要操作系统额外开辟内存。

 

2.       7大回收器的描述

 

先来看7个回收器的描述

 

======新生代的回收策略======采用标记-复制算法。

 

串行回收器:完全单线程的回收内存区域的不可达对象。串行回收过程中,所有程序都得停下来。直到串行回收过程执行完毕,用户的程序才可以继续进行。对于现在多核CPU的机器来说,这个串行的回收策略,优势越来越不明显。可用于C/S系统的客户端程序的回收器策略。

 

并行回收器:为了弥补串行回收器的不足,出现了并行回收器。多线程的方式进行回收。原理相当于多线程版本的串行回收器。利用CPU的多核优势分多个线程VS多个区域对内存进行回收而已。

 

吞吐量优先回收器:与多线程并行收集器的区别在于以吞吐量有限,保证用户运行代码的时间与内存回收的时间比,比重较大。让更多的时间去执行用户的代码。

 

======老年代的回收策略======采用标记-整理算法或者标记-清除。

 

串行老年版回收器:老年版串行回收。采用标记-整理。

 

吞吐量有限老年版回收器:吞吐量优先回收器老年版本,主要是为了将就年轻代的吞吐量优先回收器,在多线程,标记-整理算法的前提下,更保证了高吞吐量。

 

并发回收器:CMS回收器。用户的程序与垃圾回收标记程序可以同时运行。此所谓并行。短暂的停顿主要出现在标记上过程(细节是:初始标记、重新标记)。这里要声明一点,为了达到并发性。这里算法并不是使用标记-整理、而是逻辑最简单的标记-清除算法,有可能会造成老年代的内存碎片。不过可以通过别的方式进行弥补,比如标记-清除Full-GC后可以再进行一次标记-整理\压缩,避免内存的碎片过多。

 

======无论新老==============

 

新一代智能回收器:职能回收器。通过前期的回收统计、算法结果、有效地逃逸分析。来决定动态回收策略。(商业版本的JDK?到底免费不?费解?)

 

3.       算法组合以及应用场景描述

 

我们来看看新老带回收器的组合。

 

新生代+老年代组合如下:

 

1):串行回收器+串行回收器

 

使用JVM启动参数UseSerialGC启动此配置,主要场景用于单机版系统的启动配置,而且多用于老式的单核CPU的机器上。一旦遇到回收,用户自己的程序停顿时间会有比较明显的感觉。这个是JVM-Client工作模式的默认配置。

 

2):串行回收器+并行吞吐量回收器

 

JVM没有此搭配,从老年代并行吞吐量回收器的特点来说。并行吞吐量回收器出现的目的是为了让整个回收过程体现“吞吐量优先”的原则。而串行回收器作为新生代,而新生代回收的频率又是最大的。每次新生代回收,CPU都要为回收程序服务,用户程序都需要暂时停止。“吞吐量优先”的名称,是在名不副实。所以,JVM根本就没将这2个回收器配合使用。

 

3):串行回收器+并发回收器

 

原则上JVM规范指出串行回收器可以与并发回收器配合使用,但是在JVM启动参数中并没有具体的使用此配合的参数。原因待查……

 

4):并行回收器+串行回收器

 

使用启动参数UseParNewGC参数,可以使用此组合。一般情况下是在频率较高的年轻代不想消耗太多的时间,采用多线程的方式。在老年代并不使用多线程并行回收器,发生FullGC的时候,依然会阻塞很久。这种场景一般是年轻代的大小并不比老年代的大小小太多。而大家都知道一般在年轻代发生回收的频率是十分频繁的。发生在老年代的回收频率是不频繁的。如果应用大多数都是朝不保夕的对象,这种场景使用此配合。不过真的出现这种场景,您的应用程序可能在方法使用的时候多注意别创建无意义的多余对象。这个是JVM-Server工作模式的默认配置。

 

5):并行回收器+并行吞吐量回收器

 

JVM无此配置规范。

 

6):并行回收器+并发回收器

 

使用UseConcMarkSweepGC可实现此组合。这样确实是达到了一种几乎很快速的回收组合。新生代多线程标记-复制,老年代并发执行回收和用户线程同时运行。这样的组合对于老年代空间十分大,又是多核CPU的情况下,效果尤为明显。

 

对于互联网网站特别适用。但是在单核32位机器的硬性条件下,fullgc容易发生进程crash

 

7):并行吞吐量回收器+串行回收器

 

在老年代尚未出现并行吞吐量回收器的时代,这个并行吞吐量回收器只能和串行回收器配合。给人的感觉“吞吐量优先”有点名不副实。时间效率都被老年代给占用了。使用UseParallelGC可以使用这个配置玩玩。

 

8):并行吞吐量回收器+并行吞吐量回收器

 

使用UseParallelOldGC可以使用此组合。基本上这种组合适用于50%的应用场景。以用户的程序执行为优先。新生代、老年代都是以吞吐量优先的模式运行,随着程序运行时间越长,效果越明显。带来的唯一效率就是CPU、内存、内存IO读写需要付出更多的代价。不过对于用户程序运行比较敏感的系统,就不是问题了。

 

注:如果对于JVM的回收策略不是特别拿捏得当,使用这种组合,没准儿是最好的。

 

9):并行吞吐量回收器+并发回收器

 

JVM无此组合。



 
 

说明:

 

  • UseSerialGC:"Serial" + "Serial Old"
  • UseParNewGC:"ParNew" + "Serial Old"
  • UseConcMarkSweepGC:"ParNew" + "CMS" + "Serial Old". "CMS" is used most of the time to collect the tenured generation. "Serial Old" is used when a concurrent mode failure occurs.
  • UseParallelGC:"Parallel Scavenge" + "Serial Old"
  • UseParallelOldGC:"Parallel Scavenge" + "Parallel Old"

 

如果GC算法参数搭配不合理,会出现类似错误:

 

写道

 

Conflicting collector combinations in option list; please refer to the release notes for the combinations allowed
Could not create the Java virtual machine.

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值