`

【Java 8 GC 调优】“代”(Generation)

    博客分类:
  • Java
 
阅读更多

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

当一个对象不能被程序中的任何指针访问时,会被认为是垃圾。最简单的GC算法会遍历每个可到达的对象。任何遗留的对象都被视为垃圾。这种方法所用的时间与活动对象的数量成正比。这使得大型应用程序无法维护大量活动数据。
 

JVM 融合了许多不同的GC算法。这些算法通过“代际收集”(Generational Collection)结合在一起。与“检查堆中每个活动对象”的GC不同,“代际收集”利用了大多数应用程序中几个根据经验观察到的属性,以最小化回收垃圾对象所需的工作。这些观察到的属性中最重要的是“弱世代假设”(Weak Generational Hypothesis),即 大多数对象只能存活很短的一段时间
 

下图中的蓝色区域就是对象寿命的典型分布。

  • X轴 是 以分配的字节为单位 测量的对象寿命。
  • Y轴 上的字节数 是 具有相应寿命的对象的总字节数。
  • 左边的尖峰代表分配后不久可以回收的对象(也就是说,这些对象“死了”)。
    例如,迭代器对象通常存活于单个循环期间。
  • 有些对象会存活更长时间,因此分布向右延伸。
    例如,在初始化时通常会分配一些对象,这些对象一直存活到进程退出。
  • 在这两个极端之间,存在一些中间计算的对象。此图初始峰值右侧的肿块表示这些对象。



 

有些应用程序的对象寿命分布非常不同,但拥有这种常规分布的应用数量大得惊人。通过关注“大多数对象‘早死’”这一事实,高效的垃圾收集成为可能。
 

为了对此场景进行优化,内存 按代管理(不同的“代”存放不同年龄的对象)。“代”被填满时会发生GC。
绝大多数对象都被分配到专为年轻对象设计的池中(新生代),其大多数对象会在这里死亡。
当新生代被填满时,会引发一次只收集年轻对象的 “Minor GC”,其它“代”中的垃圾不会被回收。
如果“弱世代假设”成立 且 新生代中的大多数对象是可被回收的垃圾,那么就可以优化 Minor GC。这种回收方式的成本与被检测的存活对象数量成正比,全都是死对象的 “新生代” 回收非常快。
在每次 Minor GC 中通常会有部分存活的对象从新生代被移到 “老年代”(Tenured Generation)。
最终老年代也会被填满并被回收,即 Major GC,整个堆都会被回收。
Major GC 的持续时间通常比 Minor GC 要长得多,因为涉及的对象数量要多得多。
 

正如《Ergonomics》一节所述,JVM 会动态选择GC 为不同的应用程序提供良好的性能。串行GC 是为小数据量的应用程序设计的,它的默认参数是为大多数小型应用设计的。并行GC 或 吞吐量GC 用于中大型数据量的应用程序。Ergonomics 选择的 堆大小 与 自适应(堆)大小策略特性 旨在为服务器应用提供良好的性能。这些选择在大多数情况下都很有效,但并不是所有情况。这也引出了本文的中心原则:
 

如果垃圾收集成为瓶颈,那么你很可能得 自定义堆的总大小 及 各“代”的大小。检查GC的详细输出,然后研究GC参数对性能指标的影响。

 

下图展示了“代”的默认排布(除了 并行GC 和 G1)


 

在初始化时,实际上保留了最大地址空间。但除非需要,否则不会分配物理内存。整个(堆)地址空间被分为 新生代 和 老年代 (young - tenured)。
 

  • 新生代由 Eden 和 2个Survivor 组成。
  • 大多数对象最初被分配到 Eden。
  • 任何时候都有一个 Survivor 是空的(除了GC期间),它是 Eden 中存活对象的目的地。
  • 另一个 Survivor 是下一次复制收集操作的目的地。
  • (存活)对象在两个 Survivor 之间被复制,直到它们足以被划为老年代(被复制到老年代)。

 

性能关注点

有 2个主要指标 来衡量GC性能:

  • 吞吐量。它是较长一段时间内未被用于GC的时间占比。(即,应用程序业务执行时间占总时间的百分比。)
    吞吐量包含了分配对象所花费的时间。但通常不需要分配速度。
     
  • 暂停时间。它指应用程序因垃圾收集而没有响应的时间。

用户对垃圾收集有不同的要求。如:
有些人认为Web服务的正确指标是 吞吐量,因为GC过程中的暂停可能是可以容忍的,也可能会被网络延迟掩盖。
但是在交互式图形程序中,即使是短暂的停顿也可能对用户体验产生负面影响。
 

有些用户对其它考虑因素很敏感。

  • 内存占用量(Footprint)是进程的工作集。
    可以用 页(page)和 缓存线(cache line)来衡量它。
    在物理内存很有限 或 进程很多 的系统上,内存占用量 可能会决定 伸缩性。
     
  • 及时性(Promptness)是指 从对象死亡 到 内存可用 的时间。
    它是分布式系统 与 远程方法调用(RMI) 的一个重要考虑因素。

一般来说,为特定的“代”选择大小 是在这些考虑因素之间进行权衡。如,
规模非常大的 新生代 可以最大化吞吐量,但这样的代价是 内存占用高(Footprint)、及时性差(Promptness)、停顿时间长
新生代 的停顿 可以通过缩小其规模来减少,代价就是吞吐量下降。

一“代”的大小 不影响 另一“代”的 收集频率 和 暂停时间。
 

“代”的大小选择没有一种固定的正确方法。最佳选择取决于应用程序使用内存的方式 及 用户需求。因此 JVM 对 GC 的选择并不总是最优的。我们可以通过命令行选项的来改变这些选择(《确定“代”的大小》)。
 

 

衡量

吞吐量 和 内存占用量 的最佳衡量方式是使用应用程序特有的一些指标。
 

通常:

  • Web 服务的吞吐量可以通过客户端负载生成器进行测试
  • 服务的内存占用量则可以使用操作系统的命令来获得(如,Solaris 中的 pmap)。
  • GC 导致的暂停时间 则可以通过检查 JVM 的诊断输出进行估计

通过命令行选项获得 GC 活动的输出,并评估:

-verbose:gc

此选项可以让 JVM 输出每次GC时 堆与收集操作 的信息。
如下就是某大型服务端应用的输出:

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

这些输出显示了 两次 Minor GC 及 后续的一次 Major GC。
 

$ 箭头前后的数字,如第一行的 “325407K->83000K”,分别表示 GC 前后 存活对象 的总大小。
(但是)Minor GC 之后的 大小 包括一些已经是垃圾但暂时无法回收的对象。这些对象要么包含在 老年代中,要么被老年代中的(死)对象所引用。
 

$ 后面括号中的数字,如第一行的“(776768K)”,是堆的大小。
它是Java对象可用的空间量,不需要从操作系统申请更多内存。
注意,这个数字只是一个 Survivor 的空间。除了GC期间,其它任何时刻都只有一个 Survivor 会被用于存放对象。
 

$ 最后一项,如“0.2300771 secs”,表示此次GC 所花费的时间。
 

第三行中 Major GC 的格式也一样。
 

注意:-verbose:gc 输出信息的格式在未来版本中可能会发生更改。

 

-XX:+PrintGCDetails

此选项可以让 JVM 输出GC的其它详细信息。如下就是使用 串行GC 时输出的样例:

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

这段输出表明:

  • 此次 Minor GC 恢复了 新生代 大约98% 的空间(从 64575K 到 959K),花费了 0.0457646 秒。
  • 整个堆的使用率降到了大约 51%(从 196016K 到 133633K),且在收集新生代之外有一些轻微的额外开销(最终总耗时是 0.0459067 秒)。

注意:-XX:+PrintGCDetails 输出信息的格式在未来版本中可能会发生更改。

 

-XX:+PrintGCTimeStamps

此选项会让 JVM 在每次收集开始处添加一个时间戳。这有助于了解 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]

这段输出表明:

  • 此次 GC 在应用程序运行大约111秒后开始。Minor GC 大约在同一时刻开始。
  • 还有一次 Major GC (由 Tenured 表示)。老年代的使用率降低到大约10%(从 18154K 到 2311K),花费了 0.1290354 秒。

 

  • 大小: 6.4 KB
  • 大小: 8.1 KB
分享到:
评论

相关推荐

    JVM_GC调优

    ### JVM_GC调优详解 #### 一、JVM体系结构概览 Java虚拟机(JVM)作为Java程序的运行环境,其内部结构复杂且高效。为了更好地理解JVM_GC调优,我们首先来了解一下JVM的基本组成部分。 1. **类装载器子系统(Class ...

    java gc调优

    然而,GC调优是每个Java开发者都需要面对的挑战,因为合适的GC配置可以显著提高应用的性能和稳定性。以下是对“java gc调优”这一主题的深入探讨。 首先,理解JVM内存结构是GC调优的基础。JVM内存主要分为堆内存...

    java内存参数调优技巧

    Java内存参数调优是优化Java应用程序性能的关键环节,特别是对于大型和高负载的应用,合理的内存配置可以有效地降低垃圾收集(Garbage Collection, GC)带来的压力,提高应用的响应速度和系统吞吐量。以下是一些关于...

    JVM体系结构与GC调优

    **JVM体系结构与GC调优** Java虚拟机(JVM)是Java应用程序的核心组成部分,它为Java程序提供了跨平台的运行环境。了解JVM的体系结构和垃圾收集(Garbage Collection, GC)机制对于优化Java应用性能至关重要。 **1...

    jvm-full-gc调优-jvm-full-gc.zip

    1. **理解JVM内存结构**:Java内存主要分为堆内存(Heap)和非堆内存(Non-Heap),其中堆内存又分为新生代(Young Generation)、老年代(Tenured Generation或Old Generation)和持久代(Permanent Generation或...

    JVM_GC_-调优总结.pdf

    此外,了解不同代(如新生代、旧生代和永久代)的作用及其对GC的影响,有助于更好地进行性能调优。例如,在设计系统时,可以通过调整新生代与旧生代的比例来优化GC的行为,减少暂停时间,提高系统整体的吞吐量。

    Tomcat中Java垃圾收集调优宣贯.pdf

    - `-Xmn`:设定年轻代的大小,推荐值为整个堆的3/8到4/8,这会影响Minor GC的频率和效率。 - `-XX:NewSize` 和 `-XX:MaxNewSize`:用于更精确地控制年轻代的初始和最大值。 - `-XX:CMSInitiatingOccupancyFraction`...

    Tomcat中Java垃圾收集调优分享.pdf

    在Java应用服务器,如Tomcat中,性能优化的一个关键方面是Java垃圾收集(Garbage Collection, GC)的调优。垃圾收集是Java虚拟机(JVM)自动管理内存的重要机制,它负责识别并释放不再使用的对象,以避免内存泄漏。...

    JVM垃圾回收机制与GC性能调优

    2. 经过多次GC循环后,旧域(Old Generation)可能也会满,这时触发Full GC,清理整个堆,包括年轻代和旧年代,这通常会影响应用性能,因为用户线程会暂停。 GC性能调优涉及到调整堆大小、新生代和老年代的比例,...

    Tomcat中Java垃圾收集调优[文].pdf

    2. **Tenured Generation(年老代)**:经历过多次GC仍然存活的对象会被移动到这里,通常是长期存在的对象。 3. **Perm Generation(永久保存区域)**:存储类元数据和方法信息。在Java 8及以后版本中,这部分被...

    Tomcat中Java垃圾收集调优资料.pdf

    JVM内存分为三个主要区域:年轻代(Young Generation)、年老代(Tenured Generation)和永久保存区域(Perm Generation)。年轻代主要用于存储生命周期短的对象,如临时变量;年老代存放长期存活的对象;永久保存...

    java -jvm 内存分配和jvm调优

    在Java 8之后,元空间取代了永久代,存储在本地内存中。 4. 程序计数器(PC Register):每个线程也有一个独立的程序计数器,记录当前线程正在执行的字节码指令地址。 5. 本地方法栈(Native Method Stack):支持...

    JVM、GC详解及调优_jvm_JVM、GC详解及调优_

    GC调优主要包括以下几个方面: 1. **内存配置**:调整新生代、老年代的大小,以平衡GC频率和暂停时间。 2. **GC日志分析**:通过分析GC日志,了解GC行为,找出性能瓶颈。 3. **并行与并发设置**:调整并行GC线程数...

    WebLogic调优与监控(new).ppt

    GC 调优可以 manual 设置或使用自适应 GC 调优。manual 设置包括设置 heap 的大小、垃圾回收的频率等。自适应 GC 调优可以根据系统的负载情况自动调整 GC 的参数。 6. Heap 分布 Heap 分布是 GC 调优的基础。Heap ...

    JAVA内存调优白皮书(IBM)

    2. **JVM内存结构**:重点讲解了JVM的年轻代(Young Generation)、老年代(Old Generation)和持久代(Permanent Generation或Metaspace,Java 8以后的变化)的划分,以及各自的作用和垃圾回收策略。 3. **垃圾...

    JVM GC原理, heapsize调优

    堆内存被分为几个部分,包括年轻代(Young Generation)、老年代(Old Generation)和永久代(PermGen,或者Java 8之后的元空间Metaspace),不同的对象根据其生命周期的长短被分配到不同的区域中。 3. 垃圾回收...

    jvm调优,java 虚拟机优化

    Java虚拟机(JVM)调优是提升Java应用程序性能的关键环节,主要涉及到内存管理、垃圾收集(GC)、线程调度等多个方面。JVM调优的目标是优化程序运行效率,减少不必要的系统资源消耗,特别是减少全GC(Full GC)的...

    Java内存与垃圾回收调优.docx

    在Java 8及之后版本,这部分被元空间(Metaspace)替代,以减少Full GC的影响。 Java栈内存用于存储线程中的局部变量和方法调用,每个线程都有自己的栈。而Java堆内存的大小可以通过JVM参数调整,例如-Xms和-Xmx...

Global site tag (gtag.js) - Google Analytics