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

Java 技术,IBM 风格: 垃圾收集策略,第 1 部分

    博客分类:
  • JAVA
阅读更多

可以使用 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

堆碎片化的征兆

堆碎片化最常见的征兆是过早地出现分配失败。在详细垃圾收集(-Xverbose:gc)中,这表现为尽管堆上有空闲空间,但是仍然触发了垃圾收集。另一个征兆是出现小的线程分配缓存(见 “堆锁和线程分配缓存” 小节)。可以使用 -Xtgc:freelist 来决定平均大小。IBM SDK 5 Diagnostics Guide 中解释了这两个选项(参见 参考资料)。

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

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

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

标志和扫描

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

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


图 1. 垃圾收集前后的堆布局
垃圾收集前后的堆布局 

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

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


图 2. 在 optthruput 策略中 CPU 时间在 mutator 和 GC 线程之间的分布
在 optthruput 策略中 CPU 时间在 mutator 和 GC 线程之间的分布 
mutator 与 GC 线程

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

堆锁和线程分配缓存

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

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

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





optavgpause

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

关于本系列

Java 技术,IBM 风格 系列介绍 Java 平台的 IBM 实现的最新版本。您将了解 IBM 如何实现 Java 平台 5.0 版本中的一些改进,以及如何使用 IBM 在新版本中提供的一些增值特性。

如果想对文章发表评论或提问题,请与相应的作者联系。要对这个系列的整体发表评论,可以联系系列负责人 Chris Bailey。关于这里讨论的概念的更多信息以及下载 IBM 最新版本的链接,请参见 参考资料

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 线程之间的分布
在 optavgpause 策略中 CPU 时间在 mutator 和 GC 线程之间的分布 

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






gencon

分代的垃圾收集策略考虑到了对象的生命期,并将对象放在堆的不同区域。如果应用程序中的大多数对象在年轻时就死了 (也就是说,它们不会在许多次垃圾收集中存活下来),那么使用单一的堆就会影响性能;分代的垃圾收集方式就是试图解决这个问题。

利用分代的 GC,以不同方式对待长期存活的对象和短期存活的对象。堆分割为婴儿 区域和 长存(tenured) 区域,见图 4。在婴儿区域中创建对象,如果它们存活的时间足够长,就会转移到长存区域。对象在经历了一定次数的垃圾收集之后,如果仍然存活,就会被转移。其思路是大多数对象是短期存活的;通过频繁地收集婴儿区域中的对象,这些对象就可以被释放,而不需要负担收集整个堆的成本。对长存区域的垃圾收集不会频繁进行。


图 4. gencon GC 中的新老区域
gencon 垃圾收集中的新老区域 

正如在图 4 中看到的,婴儿区域本身又分成两个区域:分配(allocate) 和幸存(survivor)。对象在分配区域中进行分配,当分配区域填满时,存活的对象被复制到幸存空间或长存空间(这取决于它们的 “年龄”)。然后,婴儿区域中的空间交换用途,分配变成幸存,幸存变成分配。由已死亡的对象占用的空间可以被新分配覆盖。婴儿收集称为清扫(scavenge);图 5 说明在这个过程中发生了什么:


图 5. GC 前后的堆布局示例
GC 前后的堆布局示例 

当分配空间满了时,触发垃圾收集。然后,追踪存活的对象并将它们复制到幸存空间。如果大多数对象已经死亡了,那么这个过程实际上成本很低。另外,已经达到复制次数阈值的对象会转移到长存空间。此后,这个对象就被称为长存的

正如分代并发 这个术语所暗示的,gencon 策略有并发成分。长存空间采用一种与 optavgpause 策略相似的方式进行并发标志,但是没有并发扫描。在并发阶段,所有分配都会导致轻微的吞吐量损失。采用这种方式时,由于长存空间收集导致的停顿时间会比较短。

图 6 显示在运行 gencon GC 时执行时间的分布:


图 6. 在 gencon 策略中 CPU 时间在 mutator 和 GC 线程之间的分布
在 gencon 策略中 CPU 时间在 mutator 和 GC 线程之间的分布 

清扫的时间很短(由红色的小框表示)。灰色表示开始并发追踪(以后是收集长存空间),其中的一些操作是并发执行的。这称为全局收集(global collection),它包括一次清扫和一次长存空间收集。进行全局收集的频繁程度取决于堆的大小和对象的生命期。长存空间收集应该相当快,因为它的大部分已经并发地收集了。








subpool

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


图 7. 按照大小排序的子池空闲块
按照大小排序的子池空闲块 

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

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




结束语

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

出处:http://www.ibm.com/developerworks/cn/java/j-ibmjava2/

分享到:
评论

相关推荐

    IBM JDK5垃圾收集策略

    在IBM JDK 5中,垃圾收集(Garbage Collection, GC)是Java平台中至关重要的一个部分,用于自动管理内存,确保程序高效运行。垃圾收集策略的选择直接影响到应用程序的性能,特别是对于WebSphere Application Server...

    IBM JDK5 垃圾收集策略

    IBM JDK5 提供了四种不同的垃圾收集(GC)策略,以适应不同类型的Java应用程序需求。这些策略包括: 1. 针对吞吐量优化(-Xgcpolicy:optthruput):这是默认策略,适用于那些对整体运行速度(吞吐量)要求高于短暂...

    JAVA连接IBM MQ代码

    1. **安装IBM MQ**: 在本地或服务器上安装IBM MQ客户端和服务器组件,确保Java绑定和JMS(Java Message Service)库可用。 2. **配置MQ队列管理器**: 创建一个队列管理器,这是IBM MQ的核心组件,负责管理和调度...

    3.1.MemoryManagement IBM风格 垃圾收集策略

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

    Java技术,IBM风格:监视和判断问题

    在Java技术,IBM风格系列的本期文章中,您将了解可以从IBM跟踪和转储引擎获得的信息。本文还将介绍DiagnosticToolkitandFrameworkforJava(DTFJ)API,可以用这个API编写代码来查询和分析诊断数据。 随着时间的推移...

    JAVA IBM MQ 接收、发送

    本篇文章将深入探讨如何使用Java API与IBM MQ进行交互,包括接收和发送消息的实例。 首先,我们需要理解IBM MQ的基本概念。MQ系列是IBM提供的消息队列服务,它通过消息模型实现了应用之间的解耦。消息队列允许应用...

    java IBM MQ 7.5.0 生产者和消费者实例

    Java IBM MQ 7.5.0 是IBM提供的消息中间件,用于在分布式系统中可靠地传输数据。MQ(Message Queue)允许应用程序通过消息传递进行通信,而不是直接调用彼此,从而提高了系统的可扩展性和解耦性。MQTT(Message ...

    IBM Java Analyzer 4.34

    3. **性能调优**:分析CPU使用率、垃圾收集频率等指标,为优化Java应用程序提供依据。 4. **可视化界面**:友好的图形用户界面使得分析过程更为直观,便于理解和解释复杂的运行时信息。 5. **报告生成**:自动生成...

    IBM马达:Kubernetes 中基于策略的资源分配

    IBM马达:Kubernetes 中基于策略的资源分配,赞!K8S系列!

    ibm_java1.6.0

    【IBM Java 1.6.0】是IBM公司推出的一款基于Java SE 6的高性能、高可靠性的Java运行环境。这个版本的IBM Java是专为Linux i386平台设计的,提供了丰富的功能和组件,以支持各种企业级应用的开发和运行。以下是关于这...

    Java下操作IBM Websphere MQ的项目案例

    1. **IBM MQ Java API**:IBM提供了JMS API的实现,允许Java开发者创建MQ连接、队列管理器、队列和通道。通过`com.ibm.mq.allclient`和`com.ibm.mq.jms`这两个库,你可以编写发送和接收消息的代码。 2. **连接配置*...

    java调用ibmmq最全版本jar包,包含connector

    Java调用IBM MQ(Message Queue)是企业级应用中常见的一种技术,用于实现应用程序之间的异步通信和消息传递。在Java环境中,IBM提供了专门的Java Connector(JMS API)来与MQ进行交互。本篇文章将深入讲解如何在...

    IBM软件培训机构java课件

    IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构java课件IBM软件培训机构...

    IBM Java JVM Diagnostic Guide

    ##### 第1章:IBM Virtual Machine for Java 的构建模块 - **Java 应用程序堆栈**:详细介绍了Java应用程序运行所需的各个层,包括应用程序本身、类库和JVM。 - **IBM Virtual Machine for Java 的组成部分**: - ...

    ibm_java(ibm课程系列)

    - **课程背景**:本课程由IBM提供,旨在为学员提供深入的Java编程知识和技术实践能力,尤其关注Java 2平台及其在企业级应用中的角色与功能。 - **课程目标**: - 掌握Java 2平台的不同版本(J2ME、J2SE、J2EE)及其...

    JAVA调用IBM Notes的工具包

    JAVA调用IBM Notes的工具包,基本可以实现在java中操作Notes,适用的Notes版本为9.0,其它版本暂时未知,有兴趣的可以测试一下

    IBM--JAVA编程(中文)

    【IBM--JAVA编程(中文)】是一门专为学习IBM平台上的JAVA编程设计的课程,适合初学者和有一定基础的开发者。此课程采用中文教学,旨在帮助学员掌握JAVA语言的核心概念,以及在IBM环境中进行应用开发的技能。通过...

Global site tag (gtag.js) - Google Analytics