Java垃圾回收精粹 — Part3

Java垃圾回收精粹分4个部分,本篇是第3部分。在第3部分里介绍了串行收集器、并行收集器以及并发标记清理收集器(CMS)。

串行收集器(Serial Collector)

串行收集器是最简单的收集器,对于单处理器系统真是绝佳上选。当然,它也是所有收集器里面最不常用的。串行收集器使用一个单独的线程进行收集,不管是次要收集还是主要收集。在年老区中分配的对象使用一个简单的凹凸指针算法(bump-the-pointer algorithm)即可。当tenured space填满后会触发主要回收。

译注:按照这种技术,JVM内部维护一个allocatedTail指针,始终指向先前已分配对象的尾部。当新的对象分配请求到来时,只需检查代中剩余空间,即从allocatedTail到代尾geneTail是否足以容纳该对象,并在“满足”的情况下更新allocatedTail指针和初始化对象。

并行收集器(Parallel Collector)

并行收集器有两种形式:一种并行收集器(-XX:+ UseParallelGC)在次要回收中使用多线程来执行,在主要回收中使用单线程执行;另一种是从Java 7u4开始默认使用的并行旧生代收集器(Parallel Old collector )(XX:+UseParallelOldGC),在次要回收和主要回收均使用多线程。在tenured space分配的对象使用简单的凹凸指针(bump-the-pointer)算法即可。当年老区填满后会触发主要回收。

在多处理器系统上,并行旧生代收集器是吞吐量最大的收集器,只有收集开始时才会影响到正在运行的程序。然后使用最高效算法、多个并行线程进行收集。这使得并行旧生代收集器非常适合批处理应用。

回收年老代的成本受存留对象数量影响较大,受堆大小影响较小。要提高并行旧生代收集器的搜集效率、提供更大的吞吐量,需要更大的内存、更长的回收时间、更少的收集时暂停。

这被期望成为最快的次要回收。因为在这个收集器里,到年老区的晋升是一个简单的凹凸指针(bump-the-pointer)和复制操作。

对于服务器应用程序来说,并行旧生代收集器必须是垃圾收集的第一站。如果主要回收的暂停超过了应用程序的容忍下限,需要考虑使用与应用程序并发执行的收集器来收集年老对象。

注意:在现在的硬件条件下,对年老代的压缩每GB的存活对象预计需要暂停一到五秒。

注意:在多插槽的CPU的服务器应用程序中设置“-XX:+ UseNUMA”,可以通过并行收集器能获得更好的性能。这是因为是在线程本地的CPU插槽上分配给Eden内存。遗憾的是其他收集器不支持这个功能。

CMS(并发标记清理收集器,Concurrent Mark Sweep)

CMS(-XX:+ UseConcMarkSweepGC)收集器在年老代中使用,专门收集那些在主要回收中不可能到达的年老对象。它与应用程序并发运行,在年老代中保持一直有足够的空间以保证不会发生年轻代晋升失败。

晋升失败将会触发一次FullGC,CMS会按照下面几个步骤处理:

  1. 初始化标记:寻找GC根。
  2. 并发标记:标记所有从GC根开始可到达的对象。
  3. 并发预清理:检查被更新过的对象引用和在并发标记阶段晋升的对象。
  4. 重标记:捕捉预清洁阶段开始更新的对象引用。
  5. 并发清理:通过回收被死对象占用的内存更新可用空间列表。
  6. 并发重置:重置数据结构为下一次运行做准备。

当年老对象变得不可访问时,占用空间会被CMS回收并且放入到空闲空间列表中。当晋升发生的时候,会查询空闲空间列表,为晋升对象找到大小合适的位置。这增加了晋升的成本,因而相比并行收集器也增加了次要收集的成本。

注意:CMS 不像压缩收集器,随着时间的推移会在年老代中产生碎片。对象晋升可能失败,因为一个大对象可能在年老代在找不到一块足够容身的可用空间。如果发生了这样的事,日志会记录一条“晋升失败”的消息,然后并且触发一次FullGC来压缩存活的年老对象。对于这种压缩驱动的FullGC,由于CMS使用单线程压缩,可以想见会比使用并行旧生代收集器的主要回收使用更长的暂停时间。

CMS尽可能的与应用程序并发运行,这里面有几层含义。首先,CPU的时间会被收集器占用,因此CPU可用于应用程序的时间片减少。CMS消耗的时间量与到tenured space(老年区)的对象晋升量呈线性正相关。其次,对于并发GC周期中的某些阶段,所有的应用线程必须到达一个安全点,比如标记GC根并执行并行的重标记来检查更新。

注意:如果一个应用程序年老区的对象发生非常明显的变化,重新标记阶段将非常耗时,在极端情况下,它可能比一个完整的并行旧生代收集器的压缩时间还要长。

CMS要想降低FullGC的频率,可以通过降低吞吐量、使用更耗时的次要回收以及占用更大的空间的方式实现。 根据不同的晋升率,吞吐量会比并行收集少10%-40%。CMS同样要求多于20%的空间来存放额外的数据结构和“漂浮垃圾(floating garbage)”,漂浮垃圾是在并发标记阶段丢掉的,扔给下一个收集周期处理的对象。

高晋升率以及由此产生的碎片,有时候可以通过增加新生代和年老代空间的大小来减少。

注意:当CMS回收的空间不能满足晋升需求的时候,它可能遇到“并发模式失败(concurrent mode failures)”,在日志中可以找到记录。产生这种情况可能是因为是收集得太迟,这样可以通过调整策略来解决。也可能是收集的空间空闲率跟不上高的晋升率或则某些应用超高的对象更新率。如果你程序的晋升率和更新率太高,你可能需要改变你的应用程序来减少晋升的压力。给CMS加大内存有时候会使情况更糟,因为这需要扫描更多的内存。

时间: 2024-10-26 12:19:56

Java垃圾回收精粹 — Part3的相关文章

Java垃圾回收精粹 — Part1

Java垃圾回收精粹分4个部分,本篇是第1部分.在第1部分里介绍了权衡点.对象生命周期以及全局暂停事件. 串行.并行.并发.CMS.G1.年轻代(Young Gen).新生代(New Gen).旧生代(Old Gen).持久代(Perm Gen).伊甸区(Eden).年老区(Tenured).幸存区(Survivor Spaces).安全点(Safepoints)以及数百种JVM启动参数.当你试图调整垃圾回收器使你的Java应用程序获得需要的吞吐量和延迟时,这些会难倒你吗?如果回答是,你也不必担

Java垃圾回收精粹 — Part4

Java垃圾回收精粹分4个部分,本篇是第4部分.在第4部分里介绍了G1收集器.其他并发收集器以及垃圾收集监控和调优. Garbage First (G1) 收集器 G1 (-XX:+UseG1GC)收集器是一个新的收集器.G1随Java 6发布,在Java 7U4中得到正式支持.它是一个部分并发的收集算法,通过尝试小量增加全局暂停的方式压缩年老区,将FullGC降到最低.因为碎片引起的FullGC正是CMS的一大麻烦.G1也是分代收集器,但是它与其他收集器使用不同的堆组织方式.根据不同的用途,G

Java 垃圾回收机制(早期版本)

Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但是速度很慢的垃圾回收技术. 每个对象都含有要给引用计数器,当有引用连接至对象时,引用计数+1. 当引用离开作用域或者被置为null时,引用计数-1. 当发现某个对象的引用计数为0时,就释放其占用的空间.   这种方法开销在整个程序生命周期中持续发生,并且该方法有个缺陷,如果对象之间存在循环引用,可能

Java垃圾回收机制的工作原理

Java垃圾回收机制的工作原理 [博主]高瑞林 [博客地址]http://www.cnblogs.com/grl214 一.Java中引入垃圾回收机制的作用 当我们建完类之后,创建对象的同时,进行内存空间的分配,为了防止内存空间爆满,java引入了垃圾回收机制,将不再引用的对象进行回收,释放内存,循环渐进,从而防止内存空间不被爆满. 1.垃圾回收机制的工作原理 创建的对象存储在堆里面,把堆比喻为院子中的土地,把对象比喻为土地的管理者,院子比喻为java虚拟机,当创建一个对象时,java虚拟机将给

Java GC专家系列2:Java 垃圾回收的监控

这是”成为GC专家系列”文章的第二篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.到目前为止,你应该已经了解了JDK 7中的5种GC类型,以及每种GC对性能的影响. 在本篇中,我将介绍JVM在真实环境中如何运行GC的. 什么是GC监控 GC监控 指的是在运行时跟踪JVM运行GC的过程.例如,通过GC监控,我们能找出: 何时新生代的对象会被移动到老年代,有多少对象被移到了老年代. 何时stop-the-world发生以及持续时间. 通

Java深度历险(四)——Java垃圾回收机制与引用类型

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题.一个常见的问题是会产生“悬挂引用(dangling references)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果.开发人员有可能忘记显式的调用释放内存

java 垃圾回收机制

1. java的垃圾回收机制主要工作包括:确定哪些对象属于垃圾,回收无用的对象占用的空间,使堆中的存活对象紧密排列. 2. Java垃圾回收的算法: 引用计数(基本不用): 当引用指向一个对象时,该对象的引用计数器+1: 当引用离开对象挥着被标记为null时,引用计数器-1: 当引用计数器为0时,释放对象占用的空间. 缺陷:如果对象间存在循环引用,可能会发生对象无法被回收的情况. 停止-复制: 对象发现: 从堆栈和静态存储区出发遍历所有引用,找到引用的对象以及该对象包含的所有引用,从而找到所有活

Java垃圾回收介绍(译)

在Java中,对象内存空间的分配与回收是由JVM中的垃圾回收进程自动完成的.与C语言不同的是,在Java中开发者不需要专门为垃圾回收写代码.这是使Java流行的众多特征之一,也帮助了程序员写出了更好的Java应用. 这是一个四部分的系列教程,以了解在Java中垃圾回收的基础, Java垃圾回收介绍 Java垃圾回收如何工作? Java垃圾回收器的类型 监控和分析Java垃圾回收 这篇教程是系列中的第一部分.本篇中会解释一些基本术语,像JDK,JVM,JRE,HotSpot VM,然后去理解JVM

Java垃圾回收机制以及内存泄漏

原文地址 前言 在segmentfault上看到一个问题:java有完善的GC机制,那么在java中是否会出现内存泄漏的问题,以及能否给出一个内存泄漏的案例.本问题视图给出此问题的完整答案. 垃圾回收机制简介 在程序运行过程中,每创建一个对象都会被分配一定的内存用以存储对象数据.如果只是不停的分配内存,那么程序迟早面临内存不足的问题.所以在任何语言中,都会有一个内存回收机制来释放过期对象的内存,以保证内存能够被重复利用. 内存回收机制按照实现角色的不同可以分为两种,一种是程序员手动实现内存的释放