`

JVM实用参数(五)新生代垃圾回收

阅读更多

本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation)。首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数。

单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作。新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法)

通过广泛研究面向对象实现的应用,发现一个共同特点:很多对象的生存时间都很短。同时研究发现,新生对象很少引用生存时间长的对象。结合这2个特点,很明显 GC 会频繁访问新生对象,例如在堆中一个单独的区域,称之为新生代。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”。

 

SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。

基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。

https://blog.codecentric.de/files/2011/08/young_gc.png
https://blog.codecentric.de/files/2011/08/young_gc.png
上图演示GC过程,黄色表示死对象,绿色表示剩余空间,红色表示幸存对象

总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,如果对象存活到适当的年龄,会被移动到老年代。当对象在老年代死亡时,就需要更高级别的GC,更重量级的GC算法(复制算法不适用于老年代,因为没有多余的空间用于复制)

现在应该能理解为什么新生代大小非常重要了(译者,有另外一种说法:新生代大小并不重要,影响GC的因素主要是幸存对象的数量),如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。我们需要找到一个合适大小,不幸的是,要想获得一个合适的大小,只能通过不断的测试调优。这就需要JVM参数了

-XX:NewSize and -XX:MaxNewSize
就像可以通过参数(-Xms and -Xmx) 指定堆大小一样,可以通过参数指定新生代大小。设置 XX:MaxNewSize 参数时,应该考虑到新生代只是整个堆的一部分,新生代设置的越大,老年代区域就会减少。一般不允许新生代比老年代还大,因为要考虑GC时最坏情况,所有对象都晋升到老年代。(译者:会发生OOM错误) -XX:MaxNewSize 最大可以设置为-Xmx/2 .

考虑性能,一般会通过参数 -XX:NewSize 设置新生代初始大小。如果知道新生代初始分配的对象大小(经过监控) ,这样设置会有帮助,可以节省新生代自动扩展的消耗。

-XX:NewRatio
可以设置新生代和老年代的相对大小。这种方式的优点是新生代大小会随着整个堆大小动态扩展。参数 -XX:NewRatio 设置老年代与新生代的比例。例如 -XX:NewRatio=3 指定老年代/新生代为3/1. 老年代占堆大小的 3/4 ,新生代占 1/4 .

如果针对新生代,同时定义绝对值和相对值,绝对值将起作用。下面例子:
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp

以上设置, JVM 会尝试为新生代分配四分之一的堆大小,但不会小于32MB或大于521MB

在设置新生代大小问题上,使用绝对值还是相对值,不存在通用准则 。如果了解应用的内存使用情况,设置固定大小的堆和新生代更有利,当然也可以设置相对值。如果对应用的内存使用一无所知,正确的做法是不要设置任何参数,如果应用运行良好。很好,我们不用做任何额外动作.如果遇到性能或OutOfMemoryErrors, 在调优之前,首先需要进行一系列有目的的监控测试,缩小问题的根源。

-XX:SurvivorRatio
参数 -XX:SurvivorRatio 与 -XX:NewRatio 类似,作用于新生代内部区域。-XX:SurvivorRatio 指定伊甸园区(Eden)与幸存区大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸园区(Eden)是 幸存区To 大小的10倍(也是幸存区From的10倍).所以,伊甸园区(Eden)占新生代大小的10/12, 幸存区From和幸存区To 每个占新生代的1/12 .注意,两个幸存区永远是一样大的..

设定幸存区大小有什么作用? 假设幸存区相对伊甸园区(Eden)太小, 相应新生对象的伊甸园区(Eden)永远很大空间, 我们当然希望,如果这些对象在GC时全部被回收,伊甸园区(Eden)被清空,一切正常.然而,如果有一部分对象在GC中幸存下来, 幸存区只有很少空间容纳这些对象.结果大部分幸存对象在一次GC后,就会被转移到老年代 ,这并不是我们希望的.考虑相反情况, 假设幸存区相对伊甸园区(Eden)太大,当然有足够的空间,容纳GC后的幸存对象. 但是过小的伊甸园区(Eden),意味着空间将越快耗尽,增加新生代GC次数,这是不可接受的。

总之,我们希望最小化短命对象晋升到老年代的数量,同时也希望最小化新生代GC 的次数和持续时间.我们需要找到针对当前应用的折中方案, 寻找适合方案的起点是 了解当前应用中对象的年龄分布情况。

-XX:+PrintTenuringDistribution
参数 -XX:+PrintTenuringDistribution 指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布。例如:
Desired survivor size 75497472 bytes, new threshold 15 (max 15)
- age 1: 19321624 bytes, 19321624 total
- age 2: 79376 bytes, 19401000 total
- age 3: 2904256 bytes, 22305256 total

第一行说明幸存区To大小为 75 MB. 也有关于老年代阀值(tenuring threshold)的信息, 老年代阀值,意思是对象从新生代移动到老年代之前,经过几次GC(即, 对象晋升前的最大年龄). 上例中,老年代阀值为15,最大也是15.

之后行表示,对于小于老年代阀值的每一个对象年龄,本年龄中对象所占字节 (如果当前年龄没有对象,这一行会忽略). 上例中,一次 GC 后幸存对象大约 19 MB, 两次GC 后幸存对象大约79 KB , 三次GC 后幸存对象大约 3 MB .每行结尾,显示直到本年龄全部对象大小.所以,最后一行的 total 表示幸存区To 总共被占用22 MB . 幸存区To 总大小为 75 MB ,当前老年代阀值为15,可以断定在本次GC中,没有对象会移动到老年代。现在假设下一次GC 输出为:

Desired survivor size 75497472 bytes, new threshold 2 (max 15)
- age 1: 68407384 bytes, 68407384 total
- age 2: 12494576 bytes, 80901960 total
- age 3: 79376 bytes, 80981336 total
- age 4: 2904256 bytes, 83885592 total

对比前一次老年代分布。明显的,年龄2和年龄3 的对象还保持在幸存区中,因为我们看到年龄3和4的对象大小与前一次年龄2和3的相同。同时发现幸存区中,有一部分对象已经被回收,因为本次年龄2的对象大小为 12MB ,而前一次年龄1的对象大小为 19 MB。最后可以看到最近的GC中,有68 MB 新对象,从伊甸园区移动到幸存区。

注意,本次GC 幸存区占用总大小 84 MB -大于75 MB. 结果,JVM 把老年代阀值从15降低到2,在下次GC时,一部分对象会强制离开幸存区,这些对象可能会被回收(如果他们刚好死亡)或移动到老年代。

-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
参数 -XX:+PrintTenuringDistribution 输出中的部分值可以通过其它参数控制。通过 -XX:InitialTenuringThreshold 和 -XX:MaxTenuringThreshold 可以设定老年代阀值的初始值和最大值。另外,可以通过参数 -XX:TargetSurvivorRatio 设定幸存区的目标使用率.例如 , -XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%。

有多种方式,设置新生代行为,没有通用准则。我们必须清楚以下2中情况:
1 如果从年龄分布中发现,有很多对象的年龄持续增长,在到达老年代阀值之前。这表示 -XX:MaxTenuringThreshold 设置过大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多对象年龄从未大于1.应该看下幸存区的目标使用率。如果幸存区使用率从未到达,这表示对象都被GC回收,这正是我们想要的。 如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率。

-XX:+NeverTenure and -XX:+AlwaysTenure
最后,我们介绍2个颇为少见的参数,对应2种极端的新生代GC情况.设置参数 -XX:+NeverTenure , 对象永远不会晋升到老年代.当我们确定不需要老年代时,可以这样设置。这样设置风险很大,并且会浪费至少一半的堆内存。相反设置参数 -XX:+AlwaysTenure, 表示没有幸存区,所有对象在第一次GC时,会晋升到老年代。
没有合理的场景使用这个参数。可以在测试环境中,看下这样设置会发生什么有趣的事.但是并不推荐使用这些参数.

结论
适当的配置新生代非常重要,有相当多的参数可以设置新生代。然而,单独调整新生代,而不考虑老年代是不可能优化成功的。当调整堆和GC设置时,我们总是应该同时考虑新生代和老年代。

在本系列的下面2部分,我们将讨论 HotSpot JVM 中老年代 GC 策略,我们会学习“吞吐量GC收集器” 和 “并发低延迟GC收集器”,也会了解收集器的基本准则,算法和调整参数.

分享到:
评论

相关推荐

    简单实用JVM参数配置

    4. 垃圾回收堆:存储对象实例,分为新生代、老生代和持久代。 5. 方法域:存储类信息、常量、静态变量等。 在面临`OutOfMemoryError`时,通常需要关注以下几个方面进行JVM参数调整: 1. **新生代**:新生代的Eden...

    jvm调优实用工具.rar

    合理的内存分配可以避免频繁的垃圾回收,提高应用响应速度。 2. **垃圾收集器选择**:不同的垃圾收集器有不同的性能特性,例如Serial、Parallel、CMS、G1、ZGC等。根据应用的特性和需求,选择合适的垃圾收集器至关...

    jvm高级特性与最佳实践(第二版)

    比如,如何根据不同的应用需求选择合适的垃圾回收器,如何设置合理的JVM参数以优化性能,以及如何处理常见的JVM性能问题。这些最佳实践对于提升开发者解决实际问题的能力有着非常实际的指导作用。 需要注意的是,...

    JAVA垃圾回收机制与内存泄露问题实用.pdf

    Java垃圾回收机制是Java语言的一个重要特性,它自动管理程序中的内存分配和释放,以防止内存泄漏和资源浪费。垃圾回收的核心思想是追踪并...同时,通过调整JVM的垃圾回收参数,可以进一步优化系统的内存管理和性能。

    jvm调优的实际应用

    针对这些问题,我们可以调整JVM的启动参数,如-Xms和-Xmx设置初始堆和最大堆大小,-XX:NewRatio设定新生代与老年代的比例,-XX:SurvivorRatio设定新生代中Eden与Survivor区的比例。 在实际应用中,我们常常使用一些...

    JVM详细概述与优化大全.zip

    3. 垃圾收集调优:调整堆大小、新生代与老年代比例、设置存活阈值等参数,减少垃圾收集频率和暂停时间。 四、JVM性能监控与诊断工具 1. JVisualVM:集成多种JVM分析工具,如内存分析、线程快照、CPU剖析等。 2. ...

    mac mat jvm gc 内存分析

    JVM的垃圾收集器则负责自动回收不再使用的内存。它主要分为新生代(Young Generation)、老年代(Old Generation)和持久代(Permanent Generation)。不同的GC策略会根据应用的需求调整年轻代和老年代的大小,以及...

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

    《JVM、GC详解及调优》是一份深入解析Java虚拟机(JVM)和垃圾收集(Garbage Collection,简称GC)的详细资料。本文将根据提供的信息,深入阐述JVM的工作原理,GC的机制以及如何进行JVM的性能调优。 首先,JVM是...

    JVM—java虚拟机.pptx

    - **内存回收机制**:JVM采用了垃圾收集(Garbage Collection, GC)来自动管理内存,避免程序员手动处理内存分配和释放。GC主要分为新生代GC和老年代GC,采用不同的算法如复制算法、标记-清除算法和标记-整理算法。...

    Eclipse启动运行速度调优

    这两个参数用于启用并发标记清除(CMS)垃圾回收器和并行新生代(ParNew)垃圾回收器。CMS垃圾回收器适用于多处理器系统,能够在垃圾回收过程中保持应用程序的响应性;而ParNew则是针对新生代的并行垃圾回收器,旨在...

    JVM与性能优化知识点整理.pdf

    - **JVM**:合理配置JVM参数,优化内存分配和垃圾回收策略。 - **GC调优**:通过调整垃圾回收器类型和参数,优化GC性能。 - **调优实战**:在实际环境中针对具体场景进行性能调优。 - **存储性能优化**:优化...

    JVM内存模型和性能调优:JVM调优工具详解及调优实战:jstat – 第38篇

    垃圾回收(Garbage Collection, GC)是JVM自动管理内存的重要机制,通过`jstat -gc`命令,我们可以得到以下关键指标: 1. **年轻代**(Young Generation):包括Eden区和两个Survivor区。新创建的对象首先被分配到...

    gchisto.jar

    3. **内存使用情况**:查看堆内存的分配和使用,包括新生代、老年代的大小变化,有助于判断是否需要调整JVM内存参数。 4. **GC策略**:了解JVM采用的特定GC策略,如CMS(Concurrent Mark Sweep)、G1(Garbage-First...

    Java and the Java Virtual Machine.pdf

    - **内存调整**: 通过调整堆大小、新生代和老年代比例等参数来优化内存使用。 - **线程调度**: 支持多线程并发执行,提高程序响应速度和资源利用率。 #### 四、Java语言和JVM的关系 Java语言的设计考虑到了与JVM的...

    Java面试题总结.doc

    Java面试题总结主要涵盖Java基础知识,包括修饰符的解析、垃圾回收算法、JVM版本、字符类型、常用包的介绍以及方法的重载与重写等核心概念。下面将对这些知识点进行详细阐述: 1. **Java修饰符**:Java中的访问修饰...

    Java 解惑(细致实用)

    - 调整JVM参数,如堆大小、新生代和老年代比例,以及GC策略。 10. **并发编程最佳实践** - 如使用volatile保证可见性,使用Atomic类进行原子操作,以及线程局部存储(ThreadLocal)避免数据竞争。 以上只是部分...

    weblogic优化

    课程将覆盖Java虚拟机的基本概念,包括类加载机制、内存管理、垃圾回收等,这对于理解WebLogic Server的运行环境至关重要,因为WebLogic Server本质上是在JVM上运行的应用服务器。 ### 4. 调整Sun HotSpot JVM ###...

    写给大忙人看的JAVA核心技术【高清版带书签】

    4. **内存管理与垃圾回收**:解释了Java虚拟机(JVM)如何进行内存分配和垃圾回收,包括堆内存、栈内存以及新生代和老年代的概念。 5. **IO流**:介绍输入输出流体系,包括字节流、字符流、缓冲流、对象序列化等,...

    Oracle JRockit_The Definitive Guide

    - **垃圾回收(GC)**:提供了多种垃圾回收策略,如并行GC、自适应大小调整的新生代收集器等,以满足不同场景的需求。 - **线程与调度**:JRockit优化了线程模型,支持更高效的线程调度和同步机制。 **3. 开发与调优...

    Java的工具包Jtop.jar

    3. **内存管理**:Jtop可以帮助分析内存分配和垃圾回收的情况,包括新生代、老年代的内存使用,以及GC事件的频率和耗时,这对于优化内存配置和减少不必要的内存泄漏至关重要。 4. **JVM参数调整**:通过Jtop,用户...

Global site tag (gtag.js) - Google Analytics