WEB开发网
开发学院软件开发Java Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: ... 阅读

Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

 2009-11-04 00:00:00 来源:WEB开发网   
核心提示:可以使用 4 种不同的策略配置 IBM Developer Kit for the Java 5.0 Platform(IBM SDK)中的垃圾收集(GC),本文(关于 GC 的两篇文章的第一篇)介绍不同的垃圾收集策略并讨论它们的性质,Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

可以使用 4 种不同的策略配置 IBM Developer Kit for the Java 5.0 Platform(IBM SDK)中的垃圾收集(GC)。本文(关于 GC 的两篇文章的第一篇)介绍不同的垃圾收集策略并讨论它们的性质。在阅读本文之前,您应该对 Java 平台中的垃圾收集有基本的认识。第 2 部分将给出一种选择策略的量化方法,以及一些示例。

为什么要有不同的 GC 策略?

能够使用不同的策略使开发人员增加了对应用程序的控制能力。有许多种 GC 算法,每种算法各有优缺点,这取决于工作负载的类型。(如果您不熟悉 GC 算法的一般性主题,那么请参见 参考资料 中其他读物的链接。在 IBM SDK 5.0 中,可以用 4 种策略 之一配置垃圾收集,每种策略都使用自己的算法。默认策略对于大多数应用程序已经足够了。如果对应用程序的性能没有特别的要求,那么您对本文(和下一篇文章)的内容可能不感兴趣;可以在不改变 GC 策略的情况下运行 IBM SDK 5.0。但是,如果应用程序需要最优的性能,或者很关注 GC 停顿时间的长度,那么请读下去。您会看到最新的版本比以前的版本提供了更多选择。

那么,为什么不让 Java 运行时的 IBM 实现自动地替您做出选择呢?因为这不总是可行的。运行时很难了解您的需要。在某些情况下,希望应用程序有很高的吞吐量;而在其他情况下,希望减少停顿时间。

表 1 列出可用的策略并解释每种策略应该在何时使用。后面几节分别详细描述每种策略的性质。

表 1. IBM SDK 5.0 中的 GC 策略
策略选项描述
针对吞吐量进行优化-Xgcpolicy:optthruput (可选)默认策略。对于吞吐量比短暂的 GC 停顿更重要的应用程序,通常使用这种策略。每当进行垃圾收集时,应用程序都会停顿。
针对停顿时间进行优化-Xgcpolicy:optavgpause通过并发地执行一部分垃圾收集,在高吞吐量和短 GC 停顿之间进行折中。应用程序停顿的时间更短。
分代并发-Xgcpolicy:gencon以不同方式处理短期存活的对象和长期存活的对象。采用这种策略时,具有许多短期存活对象的应用程序会表现出更短的停顿时间,同时仍然产生很好的吞吐量。
子池-Xgcpolicy:subpool采用与默认策略相似的算法,但是采用一种比较适合多处理器计算机的分配策略。建议对于有 16 个或更多处理器的 SMP 计算机使用这种策略。这种策略只能在 IBM pSeries® 和 zSeries® 平台上使用。需要扩展到大型计算机上的应用程序可以从这种策略中受益。



一些术语的定义

吞吐量是应用程序处理的数据量。衡量吞吐量的标准是与具体应用程序相关的。

停顿时间是垃圾收集器将所有应用程序线程停下来,从而对堆进行收集所经历的时间。

在本文中,用表 1 中命令行选项中的缩写来表示这些策略:optthruput 表示针对吞吐量进行优化,optavgpause 表示针对停顿时间进行优化,gencon 表示分代并发,subpool 表示子池。

何时应该考虑采用非默认的 GC 策略?

建议您总是先使用默认 GC 策略。 在放弃默认策略之前,需要了解在哪些情况下应该采用其他策略。表 2 给出了一些原因:

表 2. 切换到其他 GC 策略的原因
切换到原因
optavgpause  我的应用程序无法忍受那么长的 GC 停顿时间。如果 GC 停顿时间能够减少的话,性能降低一些也可以接受。

我的应用程序正在一个 64 位平台上运行并使用非常大的堆 —— 超过 3 或 4GB。

我的应用程序是一个 GUI 应用程序,我很关注用户响应时间。

gencon  我的应用程序分配了许多短期存活的对象。

堆空间出现碎片化。

我的应用程序是基于事务的(也就是说,在事务提交之后,事务中的对象就不再存活了)。

subpool  在大型多处理器计算机上,我遇到了可伸缩性问题。

我要强调一点:即使出现了表 2 中提到的原因,也不足以 断言替代策略的性能会更好;它们只是提示。在所有情况下,都应该实际运行应用程序,并度量吞吐量和/或响应时间以及 GC 停顿时间。本系列的下一部分将给出进行这种测试的示例。

本文余下的几节详细描述 GC 策略之间的差异。

optthruput

optthruput 是默认策略。它是一个追踪收集器,称为标志-扫描-紧凑排列(mark-sweep-compact) 收集器。在 GC 期间总是会运行标志和扫描阶段,但是紧凑排列只在某些情况下发生。标志阶段会寻找所有存活的对象并加上标志。扫描阶段会删除所有未加标志的对象。第三个可选的步骤是紧凑排列(compaction)。在某些情况下可能会发生紧凑排列;最常见的情况是系统无法回收足够的空闲空间。

如果非常频繁地分配和释放对象,导致在堆上只留下小块的空闲内存,这时就出现了碎片化。整个堆上可能有大量的空闲空间,但是连续区域很小,导致分配失败。紧凑排列 就是将所有对象向下移动到堆的开头,一个挨一个地排列,让它们之间没有间隔空间。这会消除堆的碎片化,但这是一种代价昂贵的任务,所以只在必要时执行。

图 1 描述三个不同阶段之后的堆布局:标志、扫描和紧凑排列。深色区域表示对象,浅色区域表示空闲空间。

标志和扫描

标志 阶段遍历所有可以从线程堆栈、静态值、interned 字符串和 JNI 引用引用的对象。在这个过程中,创建一个标志位矢量,它定义所有存活对象的开头。

扫描 阶段使用标志阶段生成的标志位矢量,从而识别哪些堆存储块可以回收供以后的分配使用;这些块被添加到空闲列表中。


图 1. 垃圾收集前后的堆布局
Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

不同 GC 阶段的工作细节超出了本文的范围;我主要关注确保您理解运行时性质。关于更多细节,请阅读 Diagnostics Guide。

图 2 展示执行时间在应用程序线程(即 mutator)和 GC 线程之间如何分布。水平轴是经历的时间,垂直轴包含线程,其中 n 表示计算机上处理器的数量。对于这个图示,假设应用程序在每个处理器上使用一个线程。GC 由蓝色框表示,这说明 mutator 停止,GC 线程正在运行。这些收集线程占用 100% 的 CPU 资源,mutator 线程空闲。这个图有点儿过分笼统了,这是为了便于与本文中的其他策略进行比较。实际上,GC 的持续时间和频率依赖于应用程序和工作负载。


图 2. 在 optthruput 策略中 CPU 时间在 mutator 和 GC 线程之间的分布
Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性 
mutator 与 GC 线程

mutator 线程就是分配对象的应用程序。也可以把 mutator 称为应用程序。GC 线程是内存管理的一部分,它们执行垃圾收集。

堆锁和线程分配缓存

optthruput 策略使用连续的堆区域,应用程序中的所有线程共享这个区域。线程需要排他地访问堆,以便为新对象保留空间。这个锁称为堆锁(heap lock),它们确保任意时刻只有一个线程能够分配对象。在有多个 CPU 的计算机上,这个锁会造成伸缩性问题,因为可能同时出现多个分配请求,但是每个请求需要排他地访问堆锁。

为了缓解这个问题,每个线程保留一小块内存,称为线程分配缓存(thread allocation cache) (也称为线程局部堆,TLH)。这块存储空间是一个线程专用的,所以在其中进行分配时不使用堆锁。当分配缓存满了之后,线程使用堆锁向堆请求新的分配缓存。

堆的碎片化会妨碍线程获得较大的 TLH,所以 TLH 会很快被填满,导致应用程序线程频繁地向堆请求新的分配缓存。在这种情况下,堆锁就成了瓶颈;如果出现这样的情况,gencon 或 subpool 策略可能是比较好的替代方案。

optavgpause

对于许多应用程序,吞吐量不如响应时间那么重要。假设一个应用程序要求在 100 毫秒内完成对工作项目的处理。如果 GC 停顿时间在 100 毫秒级别,那么在 GC 期间就无法在规定时间内完成处理。垃圾收集的一个问题是,停顿时间会增加处理项目花费的最大时间。大型堆(在 64 位平台上可用)会加剧这种影响,因为垃圾收集要处理更多的对象。

optavgpause 是一个替代的 GC 策略,其设计目的是使停顿时间最小化。它并不保证特定的停顿时间,但是停顿时间会比默认 GC 策略产生的停顿时间短。它采用的思路是在应用程序运行的同时并发地执行一些垃圾收集工作。这通过两种手段来实现:

并发的标志和扫描(concurrent mark and sweep):在堆被填满以前,每个 mutator 会让出时间对对象加标志(并发标志)。GC 仍然会停止应用程序的运行,但是停顿时间会显著缩短。在 GC 之后,mutator 线程会让出时间进行扫描(并发扫描)。

后台 GC 线程:在应用程序空闲时,一个(或多个)低优先级的后台 GC 线程会执行标志工作。

根据应用程序的不同,与默认 GC 策略相比,吞吐量性能会有 5% 到 10% 的下降。

图 3 展示在使用 optavgpause 策略时执行时间在 GC 线程和 mutator 线程之间如何分布。没有显示后台追踪线程,因为它应该不会影响应用程序的性能。


图 3. 在 optavgpause 策略中 CPU 时间在 mutator 和 GC 线程之间的分布
Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

图中的灰色区域表示启用了并发追踪,每个 mutator 线程必须放弃它的一部分处理时间。每个并发阶段之后进行一次完整的垃圾收集,垃圾收集完成在并发阶段没有完成的标志和扫描工作。由此导致的停顿时间应该会比一般 GC(optthruput)短得多,这在图 3 中表现为 GC 框的时间跨度更小。从 GC 结束到并发阶段开始之间的间隔是变化的,但是这个阶段对性能没有显著影响。

optavgpause

对于许多应用程序,吞吐量不如响应时间那么重要。假设一个应用程序要求在 100 毫秒内完成对工作项目的处理。如果 GC 停顿时间在 100 毫秒级别,那么在 GC 期间就无法在规定时间内完成处理。垃圾收集的一个问题是,停顿时间会增加处理项目花费的最大时间。大型堆(在 64 位平台上可用)会加剧这种影响,因为垃圾收集要处理更多的对象。

optavgpause 是一个替代的 GC 策略,其设计目的是使停顿时间最小化。它并不保证特定的停顿时间,但是停顿时间会比默认 GC 策略产生的停顿时间短。它采用的思路是在应用程序运行的同时并发地执行一些垃圾收集工作。这通过两种手段来实现:

并发的标志和扫描(concurrent mark and sweep):在堆被填满以前,每个 mutator 会让出时间对对象加标志(并发标志)。GC 仍然会停止应用程序的运行,但是停顿时间会显著缩短。在 GC 之后,mutator 线程会让出时间进行扫描(并发扫描)。

后台 GC 线程:在应用程序空闲时,一个(或多个)低优先级的后台 GC 线程会执行标志工作。

根据应用程序的不同,与默认 GC 策略相比,吞吐量性能会有 5% 到 10% 的下降。

图 3 展示在使用 optavgpause 策略时执行时间在 GC 线程和 mutator 线程之间如何分布。没有显示后台追踪线程,因为它应该不会影响应用程序的性能。


图 3. 在 optavgpause 策略中 CPU 时间在 mutator 和 GC 线程之间的分布
Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

图中的灰色区域表示启用了并发追踪,每个 mutator 线程必须放弃它的一部分处理时间。每个并发阶段之后进行一次完整的垃圾收集,垃圾收集完成在并发阶段没有完成的标志和扫描工作。由此导致的停顿时间应该会比一般 GC(optthruput)短得多,这在图 3 中表现为 GC 框的时间跨度更小。从 GC 结束到并发阶段开始之间的间隔是变化的,但是这个阶段对性能没有显著影响。

subpool

subpool 策略可以帮助在多处理器系统上提高性能。正如前面提到的,只能在 IBM pSeries 和 zSeries 计算机上使用这种策略。堆布局与 optthruput 策略相同,但是空闲列表的结构不一样。不是为整个堆使用一个空闲列表,而是有多个列表,称为子池(subpool)。每个池按照大小进行排序。特定大小的分配请求可以由此大小的池快速地满足。使用原子性(与平台相关的)高性能指令将空闲列表项弹出这个列表,避免了串行访问。图 7 展示了如何按照大小组织空闲存储块:


图 7. 按照大小排序的子池空闲块
Java 技术,IBM 风格: 垃圾收集策略,第 1 部分: 不同的策略提供了灵活性

当 JVM 启动时或进行了紧凑排列时,不使用子池,因为有大块的堆空间空闲着。在这些情况下,每个处理器用自己专用的小型堆来满足请求。当发生第一次垃圾收集时,扫描阶段开始填充子池,后续的分配主要使用子池。

subpool 策略可以减少分配对象花费的时间。原子性指令确保在不需要全局堆锁的情况下执行分配。处理器局部的小型堆会提高效率,因为减少了缓存冲突。这会直接影响可伸缩性,尤其是在多处理器系统上。在不能使用 subpool 的平台上,分代的 GC 可以提供相似的好处。

结束语

本文描述了 IBM SDK 5.0 中的不同 GC 策略以及它们的一些性质。默认策略对于大多数应用程序是足够的;但是,在某些情况下,其他策略的性能更好。我介绍了应该考虑切换到 optavgpause、gencon 或 subpool 的一些一般场景。在对策略进行评估时,对应用程序性能进行度量是非常重要的,第 2 部分将详细演示这个评估过程。

Tags:Java 技术 IBM

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接