JavaSE HotSpot VM 8 垃圾收集调优指南-分代(4)

本节介绍Java分代收集的由来、原理和实现。分代收集利用弱代假说,将内存按代管理,不同代触发不同的垃圾回收。还提及了GC性能的测量指标,如吞吐量、停顿等,指出选择代大小需权衡多种因素,且可通过命令行选项调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本节主要讲述分代收集的由来、原理和实现。

Java SE平台的一个优势是它对开发人员隐藏了内存分配和垃圾收集的复杂性。然而,当垃圾收集是主要瓶颈时,理解这种隐藏实现的某些方面是有用的。垃圾收集器对应用程序使用对象的方式进行假设,这些假设反映在可调参数中,这些参数可以在不牺牲抽象能力的情况下进行调整,以提高性能。

当一个对象不能再被运行中的程序中的任何指针访问到时,就会被视为垃圾。最简单的垃圾回收算法会遍历每个可访问的对象。剩下的任何对象都被视为垃圾。这种方法所花费的时间与存活对象的数量成正比,这对于维护大量存活数据的大型应用程序来说是不允许的。

虚拟机包含许多不同的垃圾收集算法,这些算法使用分代收集进行组合。不成熟的垃圾收集检查堆中的每一个存活对象,而分代收集利用从大多数应用程序的经验中观察到的一些特性,以最小化回收未使用的(垃圾)对象所需的工作。这些观察到的特性中最重要的是弱代假说,该假说认为大多数对象只存活很短的时间。

图3-1“对象生命周期的典型分布”中的蓝色区域是对象生命周期的典型分布。x轴是对象寿命,以分配的字节为单位。y轴上的字节数是具有相应生存期的对象中的总字节数。左侧的尖峰代表那些分配后不久即可回收的对象(换句话说,已“死亡”)。例如,迭代器对象通常只在单个循环期间存活。

图3-1 对象生命周期的典型分布

image

图3-1 的说明1

有些对象确实寿命更长,因此分布向右延伸。例如,通常有一些在初始化时分配的对象,会一直存活,直到进程退出。介于这两个极端之间的是在某些中间计算期间存活的对象,在这里被视为初始峰值右侧的隆起。尽管有些应用程序具有非常独特的外观分布,但令人惊讶的是,大量应用程序具有这种一般形状。通过关注大多数对象朝生夕灭(die young)的这一事实,高效回收成为可能

为了优化这个场景,内存按代管理(内存池存储不同年龄的对象)。每个代满时,都会触发这个代的垃圾回收。大多数对象分配在年轻代,并且大多数都在这里死去。当年轻代被填满时,将会触发一个minor collection(只会收集年轻代,不会回收其他代的垃圾)。假设弱代假说成立的前提下,minor collections可以优化。这类回收的成本与将要被回收的对象的数量成正比。年轻代收集很快。通常,每次minor collection,年轻代中的一部分存活对象会被移动到老年代。最后,老年代(tenured generation) 也会被填满,必须被收集,进而导致一次 major collection(整个堆都会被收集),major collection通常会比minor collection持续长得多的时间,因为涉及的对象数量大得多。

如上一节人机工程所述,人机工程学动态地选择垃圾收集器,以在各种应用程序上提供良好的性能。串行垃圾收集器是为具有小数据集的应用程序设计的,它的默认参数被选择为对大多数小应用程序有效。并行(或吞吐量)垃圾收集器用于具有中到大型数据集的应用程序。人机工程学选择的堆大小参数,加上自适应大小策略的特性,旨在为服务器应用程序提供良好的性能。这些选择大多数情况下都有效,但不是所有。这就要提到本文档的中心宗旨:

注意:

如果垃圾收集成为瓶颈,则你很有可能需要自定义整个堆的大小以及各代的大小。检查垃圾收集器的详细输出,然后研究你的单个性能指标对垃圾收集器参数的敏感性。

图3-2 显示了默认的(内存)分代分布情况(并行收集器和G1除外)

图 3-2 默认分代,除了Parallel Collector和 G12

image

在初始化时,最大地址空间是虚拟保留的,除非需要,否则不会分配物理内存。为对象内存保留的完整地址空间可以分为年轻代和老年代。

年轻代由伊甸园和两个幸存者空间组成。大多数对象是最出在eden中分配的。任何时候都有一个幸存者空间是空的,作为伊甸园中任何存活对象的目的地;另一个幸存者空间是下一次复制收集期间的目的地。对象以这种方式在幸存者空间之间复制,直到它们足够老,可以终身使用(复制到老年代)。

性能考量

GC性能有两个主要的测量指标:

  • 吞吐量(Throughput)是长时间内未用于垃圾回收的时间占总时间的百分比。吞吐量包含花费在分配上的时间(但是通常不需要调整分配速率)
  • 停顿(Pauses)是指由于正在进行垃圾回收而导致应用程序无响应的时间。

用户对垃圾收集有不同的要求。例如,一些人认为web服务器的正确指标是吞吐量,因为垃圾收集期间的暂停可能是可以容忍的,或者简单地被网络延迟所掩盖。然而,在交互式图形程序中,即使是短暂的暂停也可能对用户体验产生负面影响。

一些用户对其他考虑因素敏感。Footprint(占用空间)是一个进程的工作集,以内存页和缓存行度量。在物理内存有限或进程众多的系统上,占用空间可能决定了可扩展性。及时性是指从对象死亡到它占用的内存可用之间的时间,这是分布式系统的一个重要考量因素,包括远程方法调用(RMI)。

一般来说,为特定一代选择大小是在这些考量因素之间的权衡。例如,一个非常庞大的年轻代可能会最大化吞吐量,但这样做是以占用空间、及时性和停顿时间为代价的。年轻代的停顿可以通过使用小的年轻代,牺牲吞吐量为代价,来最小化。一个代的大小不会影响其它代的收集频率和停顿时间。

没有一种正确的方法可以选择一代的大小。最佳选择取决于应用程序使用内存的方式以及用户需求。因此,虚拟机对垃圾收集器的选择并不总是最佳的,可以会被“调整代大小”一节中描述的命令行选项覆盖。

度量

吞吐量和占用空间最好使用特定于应用程序的指标来衡量。例如,可以使用客户端负载生成器测试web服务器的吞吐量,而可以使用pmap命令在Solaris操作系统上测量服务器的占用空间。而由于垃圾收集所导致的停顿,可以通过检查虚拟机本身的诊断输出,很容易地估算出来。

命令行选项-verbose:gc会使每次收集时相关的堆和垃圾收集的信息都打印出来。例如,下面是一个大型服务器应用程序的输出:

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

输出显示两个次要收集,然后是一个主要收集。箭头前后的数字(例如,第一行 325407K->83000K )分别表示垃圾收集前后存活对象的累计大小。在较小的收集之后,大小包含一些是垃圾(不再存活)但无法回收的对象。这些对象要么包含在老年代中,要么被老年代引用。

紧接着的括号中的数字(例如,第一行的(776768K))是堆的已提交大小:在不向操作系统请求更多内存的情况下,Java对象可用的空间大小。请注意,这个数字只包括一个幸存者空间。除了在垃圾收集期间,在任何给定的时间都只能使用一个幸存者空间来存储对象。

行尾的最后一项(例如,0.2300771秒)表示执行采集所需的时间,在本例中大约为四分之一秒。

第三行中主要收集的格式类似。

注意:

-verbose:gc生成的输出格式可能会在将来的版本中更改。

命令行选项-XX:+PrintGCDetails会导致打印有关收集的其他信息。这里显示了使用串行垃圾收集器的-XX:+PrintGCDetails输出的示例。

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

这表明,次要收集回收了大约98%的年轻一代,DefNew:64575K->959K(64576K),耗时 0.0457646秒(约45毫秒)。

整个堆的使用率降低到约51%(196016K->133633K(261184K)),并且根据最终时间0.0459067秒所示,表明收集有一些额外的开销(在年轻代的收集基础之上)。

注意:

-XX:+PrintGCDetails生成的输出格式可能会在未来版本中更改。

选项-XX:+PrintGCTimeStamps在每个收集的开始处添加一个时间戳。这有助于查看垃圾收集发生的频率。

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秒(约130毫秒)。


  1. x轴是“Bytes Allocated”,表示以分配的字节为单位测量的对象寿命。y轴是"Bytes surviving",是具有相应生存期的对象的总字节数。图的左三分之一标记为“次要收集”。图的右三分之二标记为“主要收集”。绘图线下方的区域为实心且蓝色。该区域表示对象寿命的典型分布。该区域在左侧达到峰值,并向右侧延伸。该图将在后续的文本中进一步描述。 ↩︎

  2. 这个图形由一行六个矩形组成。这些矩形的标签如下(从左到右):1. Eden 2. Survivor 3. Spaces 4. Virtual 5. No label 6. Virtual 矩形1至4标记为“年轻”。矩形5至6标记为“终身” ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值