`
leichenlei
  • 浏览: 123729 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

J2SE6 HotSpot垃圾回收调优(一)

 
阅读更多
官方地址:http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html

转载请注明出处,多谢。

 

 

1      介绍

       java平台标准版被广泛引用于各种应用,从桌面小程序到大型服务器上的web服务。支持不同范围的部署,javaHotSpot虚拟机实现(Java HotSpot VM)提供多种垃圾回收器,每个回收器满足不同的需求。回收器是满足不同大小应用的重要部分。然而用户,开发者和管理员需要的高性能,让他们背负了选择垃圾回收器的额外步骤。一个重大进步在J2SE5.0中去掉了这个步骤:垃圾回收器的被自动选择,基于应用程序运行的机器种类的不同。

      

       这种JVM自己选择较好的垃圾收集器是一个改善,但是不是对每个应用都是最好的选择。更严格的性能目标和其他需求可能需要明确执行回收器、调整一些参数。本文正是提供这样的信息。首先,回收器的一般功能和串行、stop-the-world收集器情况下的基本调整选项。其他回收器的特殊功能、当选择回收器需要考虑的因素。

      

       何时选择垃圾回收器有关系吗?对于一些应用,答案是永远不。即,应用可以在适当停顿、频率、持续时间的回收器下运行的很好。然而这不适合大部分种类的应用,尤其是那些大数据量,很多线程,和高事物级别。

      

       Amdahl定律显示,很多工作负载不能完美的平行执行;一些部分总是串行的,在java中也是这样的。实际上,Sun Microsystems的虚拟机 1.4不支持并行垃圾回收,so the impact of garbage collection on a multiprocessor system grows relative to an otherwise parallel application.

      

       下面的图表模拟了在一个除了垃圾回收以外的理想系统下的完美扩展。红线是一个在单个处理器的系统上的、花费1%垃圾回收时间的应用,但是在32个处理器的系统上生产力下降多余20%10%垃圾回收时间的那条线,在32个处理器的系统上下降了75%的生产力(不考虑在单处理器应用中的离谱垃圾回收时间)。

 

 

       这表明了,在小系统上是一个微不足道的速度问题,而在大系统上就成为瓶颈。然而减少这样瓶颈的小改动,可以对性能产生大的进步。对于一个足够大的系统,选择对的垃圾回收器和调整它是值得做的。

       串行收集器对小应用(在现代处理器下,堆需求接近100MB)足够了。选择其他收集器会带来额外的开销,同时/或者,复杂性是这个专业行为付出的代价。如果应用不需要替换选择器这样的专业行为,使用串行收集器。串行收集器不能满足的情况是,一个高度线性化应用运行在一个大量内存、2个以上处理器的机器。当应用运行在这样的server-class(译者:server-class就是大内存,多处理器的机器)机器上时,平行收集器被默认选择。

       这个文档使用java SE 6 Solaris操作系统上作为参考,这里的概念和建议适用于所有支持系统,包括LinuxMicrosoftWindowsSolaris Operating System (x86 Platform Edition)。另外,提及的命令行参数适用于所有系统,默认值在不同系统可能不同。

2      功效学

       效学是J2SE5引进的。功效学的目的是在少量或者不调整参数的情况下,通过自动选择回收器、堆大小、JVM启 动时的运行编译器,来提供好的性能;而不用固定的默认值。这个选择假设应用运行的机器类型是应用类型的代表(大应用会运行在大机器上)。除此之外,这样的 选择简化了调整垃圾回收的方法。在平行垃圾回收器下,用户能指定应用需要的最大暂停时间和吞吐量。这和指定堆大小形成对比。这特别适用于改善适用大型堆的 大应用的性能。更综合的功效学看Ergonomics in the 5.0 Java Virtual Machine。在使用本文做更细致的控制之前,建议尝试后面提到的功效学。

       文档包括的工效学作为平行收集器选择适当大小策略的一部分。包括为垃圾回收性能指定目标和更好调整性能的其他参数。

3      分代

       J2SE平台的其中一个优势是把开发者从复杂的内存分配和垃圾回收中解放。然而,一旦垃圾回收成为首要瓶颈,就值得理解一些它的隐藏实现。垃圾回收器对应用使用对象的方式作出假定,反应在通过参数改善性能上,而不牺牲抽象的力量。

       当对象不再从运行程序的指针可达,它被认为是垃圾最简单、直接的垃圾回收算法是遍历每个可达对象。所有留下(译者:没有遍历到)的对象就是垃圾。这个方法的消耗时间和活着的对象数量成正比,这对于维护大量活对象的大应用是高昂的(prohibitive)的。

       J2SE 1.2开始,虚拟机包含了几个集合代手机的垃圾回收算法。幼稚的垃圾回收检测堆里的每个活对象,代收集利用大多数应用的几个经验的观察属性来减少回收unused(垃圾)对象的工作。这些观察属性中最重要的是"弱代假设"weak generational hypothesis,它表明了大多数对象只有一个短的生还期。

       下图的蓝色区域是对象声明周期的典型分布。X轴是以字节分配的生命周期测量。Y轴是对象在整个生命周期中的总字节。左侧顶点可以被回收(例如已经死了)在分配不久之后。被遍历的对象,例如,经常是整个单循环整时期内活着的。

 

 

       那些活着比较长的延展到了右边。例如,有些对象最初分配,直到进程结束。两个极端之间是活在中间计算的时间的对象,即图中第一个峰值的右侧。一些应用有不同的分布,但是巨大数量的应用拥有这个形状。有效收集集中在大多数年轻时候死die-young对象上。

       对这个方案的优化,分代管理内存,或者说持有不同年龄对象的内存池。垃圾回收在每个代上,当代被填满。绝大多数对象被分配在致力于(dedicated to)年轻对象的池(年轻代),大多数对象死在这里。当年轻代被填满引起次要收集,次要收集只收集年轻代;其他代的垃圾不被回收。次要收集能被优化-假设弱代假设持有、年轻代的大多数对象是垃圾、能被回收。这样一个收集的代价是第一顺序(the first order)、被收集的活着对象的数量成正比;年轻代被死对象填满被收集是非常快的。代表性的,一些从每个次要收集中生还对象被从年轻代移动到老年代(tenured)。最后,老年代将填满、必须被收集时,发生主收集,在主收集中整个堆被收集。主收集通常比次要手机持续时间长,由于包括了相当大量的对象。

       如 上所诉,功效学动态选择垃圾回收器为了各种应用的好的性能。各种收集器为了各种小数据集应用、为大多数小应用选择默认参数。吞吐量回收器是适用于中等到大 量数据集合。功效学添加的调整大小政策,选择堆大小参数来保证服务器应用的好性能。这些选择大多数工作的很好,但是不是全部。这导致了本文的中心原则:

       如果垃圾回收变成瓶颈,你最可能自定义整个堆大小和个别代的大小一致。检查冗长的垃圾回收期输出,然后探测你的个别代性能的灵敏度以度量垃圾回收器参数。

       默认代参数是这样的(除了并行回收器的所有)

 

 

       最初,最大地址是保留的,不分配的,除非需要。为对象保留的整个的地址空间分成youngtenured代。

       年轻代由伊甸园和两个生还者区组成。大多数对象初始分配在伊甸园。其中一个生还者区保持空的,在下一个复制收集期间,作为伊甸园和另一个生还者区中活对象的去处。对象以这种方法在两个生还者之间来回复制,直到对象足够老了,就复制到老年代。

       第三个和老年代紧密联系的代是永久代,它持有虚拟机描述的、在java语言级别没有对应的对象的数据。例如,描述类和方法的对象被存储在永久代。

 

3.1 性能考虑

主要有两个垃圾回收性能指标:

 

吞吐量:一段长时间内,花费在垃圾回收之外的百分比。吞吐量包括对象分配的时间(但,对象分配速度调优一般是没必要的)。

暂停时间:应用由于垃圾回收出现不响应的时间。

 

       用户有不同的垃圾回收需求。例如,有些人为web server的合适的度量是吞吐量,因为垃圾回收的暂停时间是可以忍受的,或者简单被网络因素掩盖。然而,一个图形交互程序,更短的暂停时间才不会影响用户体验。

       一些用户还有其他考虑。Footprint is the working set of a process, measured in pages and cache lines.在有限的内存或多处理器的系统上footprint may dictate scalabilityPromptness是从一个对象死了到其内存再次可用之间的时间,这是分布式系统的一个重要考虑,包括远程方法调用(RMI)。

 

       一般,指定不同的代的不同大小,就是对上述考虑的权衡。例如,非常大的年轻代使吞吐量最大化,但是这样做却是在破坏footprintpromptness和暂停时间指标。通过使用小的年轻代来减少年轻代暂停时间。大致上,一个代的大小不会影响另一个代的收集频率和暂停时间。

       没有一个绝对正确的设置代大小的方法。最好的选择是看应用使用内存情况和用户需求。因此,虚拟机选择一个垃圾回收器并不总是最佳的,回收器的选定可以用下述额命令行选项选择。

 

3.2 度量

吞吐量和footprint对于应用时最容易用具体指标测量的,例如,web server的吞吐量可以使用客户端负载生成器,服务器的footprintSolaris系统上可以使用命令pmap。另外,垃圾回收的暂停时间可以查看虚拟机的诊断输出。

 

       命令行 –verbose:gc输出每次gc的堆和垃圾收集信息。例如,下面是一个大型服务器应用的输出:

[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]

我们看到两个次要收集,之后是一个主要收集。箭头前和后的数字表示收集前和后活对象的大小。次要收集之后的数字包括一些不能回收的垃圾对象,这些对象也被包含在老年代,或者来自老年代、持久代的引用。

       接下来()中的数字是堆大小:java对象可用空间。注意,这个数字不包括一个生还者区(survivor,因为,在任何时间,只有一个生还者区是可用的,也不包括虚拟机用来持有metadata的持久代。

       每行的最后表示执行收集花费的时间;例子中大概执行了四分之一秒。

       主要收集的格式是一样的。

       -verbose:gc的格式在今后的版本中可能改变。

 

    选项-XX:+PrintGCDetails可以打印收集的附加信息。下面是一个串行收集器的例子:

[GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs

表示次要收集回复了年轻代98%,DefNew:64575K->959K(64576K),花了0.0457646 secs (大约 45 毫秒)

       整个堆减小了51% 196016K->133633K(261184K) ,减小堆有一些微小的额外开销(译者:0.0459067-0.0457646),最终时间是0.0459067

   

    选项-XX:+PrintGCTimeStamps会在收集开始的时候加上时间戳。这对于观察GC频率很有帮助

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]

收集开始应用程序运行111秒时。次要收集开始于同样的时间。老年代的主要收集的附加信息被显示。老年代的使用减少到10% 18154K->2311K(24576K)。花了0.1290354 secs(大约130毫秒)。

      

       -verbose:gc一样,-XX:+PrintGCDetails的格式在以后的版本可能改变。

 

未完待续

  • 大小: 9.1 KB
  • 大小: 5.3 KB
  • 大小: 18.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics