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

垃圾收集器和内存分配


内存区域中:

​ 方法区和堆存在GC和OOM(OutOfMemoryErro)

​ 本地方法栈和虚拟机栈没有GC过程,但是有SOF(StackOverflow)

​ 程序计数器既没有GC也没有OOM或者SOF

垃圾回收的并行和并发

  • 并行(Parallel):指多条垃圾收集器并行工作,但用户线程处于等待状态。
  • 并发(Concurrent):指用户线程和垃圾收集线程同时执行(不一定并行,可能交替执行),垃圾收集线程执行时不会暂停用户线程。

垃圾收集器

图4

​ 上图为JDK1.7之后的HotSpot虚拟机的收集器。如果两个收集器直接存在连线,说明可以它们可以搭配使用。所处的区域表示它属于新生代收集器还是老年代收集器。

Serial收集器
  • 最基本、发展最久的收集器
  • 单线程的收集器

图5

​ Serial收集器的"单线程"不仅仅是说会单独使用一个线程完成垃圾收集工作,而是说当进行垃圾收集点时候,其他的工作线程必须停止工作,至到收集结束。因为可达性分析必须在一个能确保一致性到快照中完成,这里的一致性是指分析期间整个执行系统应该停在某个时间点。不可以出现对象引用关系的变化,如果不满足这点,那么就无法确保结果的准确性。

ParNew收集器

​ ParNew收集器是Serial收集器的多线程版本。除了使用多个线程来进行垃圾回收,其余的回收策略、收集算法等和Serial完全一致。

在CPU的环境中,parnew收集器的效率低于Serial收集器,由于存在线程交互的开销。当随着CPU核心和线程数的增加,ParNew收集器能更有效的利用系统资源进行GC。

Parallel Scavenge收集器

​ Parallel Scavenge是一个新生代的采用复制算法的多线程收集器,它的目标是达到一个可控制的吞吐量(就是CPU用于执行用户代码的时间与CPU总耗时的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。高吞吐量代表更高效的利用CPU时间,尽快的完成程序的运算任务,主要适合后台运算而不需要过多交互的任务。

​ Parallel Scavenge提供了如下参数来控制吞吐量:

  • -XX:MaxGCPauseMillis,最大收集停顿时间
    • 一个大于0的毫秒值,收集器会尽可能的在这个时间内完成垃圾收集。如果这个值设定的过小:将会增加收集的频率,导致吞吐量下降;还将缩小新生代的空间,来更快的完成GC。
  • -XX:GCTimeRatio,吞吐量大小
    • 一个(0,100)的整数,即垃圾收集时间占总时间的比率,吞吐率的倒数。默认为99.
  • -XX:+UseAdaptiveSizePolicy,一个开关参数,如果打开此参数,代表虚拟机会已经当前系统的运行情况收集性能监控信息,动态的调整新生代的大小、晋升老年代的年龄等细节参数来提供最合适的停顿时间或最大的吞吐量。
Serial Old收集器

​ Serial的老年代版本,一个单线程使用"标记-整理"算法的收集器。主要意义是给Client模式下的虚拟机使用。在Server模式,主要的用途:

  • 在JDK1.5之前与Parallel Scavenge收集器搭配使用
  • 作为CMS收集器的后备预案
Parallel Old收集器

​ Parallel Scavenge的老年代版本,使用多线程"标记-清理"算法。

CMS收集器

​ CMS(Concurrent Mark Sweep)收集器的目标是获取最短回收停顿时间。基于"标记-清除"算法实现的,它的运作过程如下:

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清除

初始标记和重新标记这两部需要"Stop The World"。初始标记仅仅是标记一下GC Roots能直接关联的对象,速度很快;并发标记就是进行GC Roots Tracing(GC根搜索)的过程;而重新标记阶段则是为了修正并发标记期间因用户程序运作到导致标记产生变动的那一部分对象的标记记录,这个停顿时间一般会比初始标记阶段稍长一些,远比并发标记时间短。

图6

​ CMS收集器的缺点:

  • 对CPU资源非常敏感。并发阶段虽然和用户线程一起执行,但是会因为占有一部分CPU字义安导致应用程序变慢,吞吐量先进。
  • 无法处理浮动垃圾。在并发处理的部分,用户线程也在同步执行,伴随程序就会产生新的垃圾,这部分垃圾产生中标记之后,CMS无法在本次收集中处理的它们。
  • "标记-清除"算法实现的收集器带来的出现大量内存碎片的问题。如果碎片过多,大对象无法得到足够大的连续空间,不得不提前进行下一次垃圾收集。
G1收集器
  • 并行与并发:充分利用多CPU、多核的硬件优势缩短停顿时间。
  • 分代收集:G1可以管理整个GC堆,但它可以采用不同的方式处理处于不同区域的对象来获取更好的收集效果。
  • 空间整合:采用"标记-整理"算法和"复制"算啊,不会产生内存碎片。
  • 可预测停顿

​ G1不像之前的收集器将收集,收集的范围是整个新生代或老年代。它将整个Java堆划分为多个大小相当的独立的区(Region),虽然保留了新生代和老年代的概念,但新生代和老年代不再是物理隔离的,它们都是一部分Region(不需要连续)的集合。

​ 因此,G1不需要对整个Java堆进行垃圾收集。G1会跟踪每一个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需要的经验值),后天维护一个悠闲列表,每次更加允许的收集时间,优先回收价值最大的Region(Garbage-First)。这种方式保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。

​ G1收集器的运作大致分为一下步骤:

  • 初始标记:标记GC Roots能直接关联到的对象,并修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发时,能在正确可用的Region中穿件新对象。
  • 并发标记:从GC Roots开始对堆中的对象进行可达性分析,确认存活对象。
  • 最终标记:修正在并发标记期间因用户程序继续运转而导致标记产生变动的那一部分标记记录。
  • 筛选回收:对各Region的回收价值和成本进行排序,依据用户期望的GC停顿时间指定回收计划。

图7

​ G1在收集某一个Region的时候,需要判断对象是否被其他的Region所引用(其他收集器也存在这个问题,只不过是新生代和老年代之间。而G1由于存在多个Region,所以这个问题就尤为突出)。

​ 虚拟机使用Remembered Set来避免全堆扫描。G1的每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference引用的队员进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region中,如果是,就把相关的信息记录到被引用对象所属Region的Remembered Set中。当进行收集的时候,在GC从根阶段的枚举范围加入Remembered Set就可保证不对全堆扫描也不会有遗漏。

垃圾回收器的比较

内存分配

  • 对象优先在Eden分配:大多数情况,新创建的对象分配在Eden区,当Eden区没有足够的空间的时候,虚拟机将发起一次Minor GC。

​ 新生代GC(minor GC):指的发生在新生代的垃圾收集动作,因为Java中大多数对象都具有朝生夕死的特性,所以Minor GC非常频繁,一般回收的速度也比较快。

​ 老年代(Major GC/Full GC):指发生在老年代的GC,出现Major GC的时候经常会伴随至少一次的Minor GC。Major GC的速度一般会比Minor GC慢十倍以上。

  • 大对象直接进入老年代:指很长的字符串和数组等对象,虚拟机通常需要提前触发GC来获取足够的空间来分配给它们。

-XX:PretenureSizeThreshold 令大于这个设置值的对象直接分配来老年代。只对Serial和ParNew收集器有效。

  • 长期存活的对象将进入老年代:虚拟机给每个对象定义了一个对象年龄计数器。出生在Eden区域的对象,每经历一次Minor GC任然存活,那么年龄就加一。当年龄达到一定的数值(默认15),就晋升到老年代。

-XX:MaxTenuringThreshold参数来设定晋升老年代的年龄阈值。

  • 动态对象年龄判定:对象从Eden区晋升到老年代,并非年龄必须到达阈值。如果Survivor空间相同年龄所有对象大小之和大于Survivor空间的一半那么年龄大于或等于该年龄的对象就可以直接进入老年代。

  • 空间分配担保:Minor GC之前,虚拟机会坚持老年代中最大可用的连续空间是否大雨新生代所有对象的空间总和。

    • <1>条件成立:那么此次Minor GC是安全的。
    • <2>条件不成立:虚拟机查看HandlePromotionFailure设置值是否允许担保失败。
      • <2.1>:允许,如果老年代最大可用连续空间大于晋升老年代对象的平均大小,将尝试进行Minor GC,单这次Minor GC是有风险的。
      • <2.2>不允许或者允许的情况下但老年代最大可用连续空间小于于晋升老年代对象的平均大小,那么改为进行一次Full GC。

存在的风险:新生代使用复制收集算法。为了内存利用率,只预留一个Survivor来进行轮换。如果Minor GC后存活的对象占用空间之和大于一个Survivor空间,就需要将多出对象存放在老年代,就是老年代分配担保。如果老年代也没有足够的空间存储那就进行Full GC。因为一次Minor GC之后存活的对象是不确定的,所以和老年代的空间比较也是一种概率的比较,也就意味着有可能会担保失败(Minor GC存活的对象徒增),如果失败,那也转而进行Full GC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值