读Java性能权威指南(第2版)笔记18_垃圾回收E

1. 回收

1.1. 找到不使用的对象

1.2. 释放它们的内存

1.3. 压缩堆

1.4. 合在一起称为回收

2. Throughput回收器

2.1. 工作细节比较简单

2.1.1. 可以在同一个GC周期内完成回收

2.1.2. 在单次操作过程中回收新生代或老年代

2.2. Minor GC

2.2.1. 当Eden空间被填满时,新生代回收就会发生

2.2.2. 新生代回收会将所有的对象移出Eden空间

  • 2.2.2.1. Eden空间一般是空的

  • 2.2.2.2. 不认为它被压缩了

2.2.3. 另一些被移到老年代

2.2.4. 还有大量对象因不再使用而被丢弃

2.3. Full GC

2.3.1. 老年代回收会将新生代中的所有对象释放出来

2.3.2. 只有老年代中还剩下活跃引用的对象

2.3.3. 所有这些对象占用的空间都会被压缩

  • 2.3.3.1. 只占用老年代的开始部分

  • 2.3.3.2. 其余部分是空闲的内存空间

2.4. 优化

2.4.1. 围绕停顿时间进行的

2.4.2. 权衡

  • 2.4.2.1. 时间和空间的取舍

    • 2.4.2.1.1. 更大的堆会消耗机器上更多的内存

    • 2.4.2.1.2. 应用程序会有更高的吞吐量

  • 2.4.2.2. 执行GC所需的时间长度

    • 2.4.2.2.1. 增加堆的大小可以减少Full GC停顿的次数

    • 2.4.2.2.2. GC时间较长,可能延长平均响应时间

    • 2.4.2.2.3. 为了缩短GC停顿时间

    • 2.4.2.2.3.1. 将更多的堆分配给新生代而不是老年代

    • 2.4.2.2.3.2. 会增加老年代回收的频率

2.5. 自适应大小

2.5.1. 重新调整堆(和代)的大小,以实现其停顿时间的目标

  • 2.5.1.1. 并不总是堆越大就越好

2.5.2. 当给定的停顿时间目标不切实际时,应用程序的行为会更糟糕

2.5.3. -XX:MaxGCPauseMillis=N

  • 2.5.3.1. 用于设定应用程序可接受的最大停顿毫秒数

  • 2.5.3.2. 默认情况下,不设置这个标志

  • 2.5.3.3. 适用于Minor GC和Full GC

  • 2.5.3.4. 如果使用了一个非常小的值,那么应用程序最终会得到一个非常小的老年代

    • 2.5.3.4.1. 导致JVM非常频繁地执行Full GC,应用程序的性能会很糟糕

2.5.4. GC中花费总时间3%到6%的应用程序,运行效果相当好

2.5.5. -XX:GCTimeRatio=N

  • 2.5.5.1. 设定你希望应用程序在GC上花费的时间(相对于应用程序线程应该运行的时间)

  • 2.5.5.2. 标志的默认设置很适用

    • 2.5.5.2.1. 默认值是99

    • 2.5.5.2.2. 在没有其他目标的情况下,推荐时间比例设置为19(GC时间占5%)

  • 2.5.5.3. ThroughputGoal=1-(1÷ (1+GCGCTimeRatio))

3. CMS回收器

3.1. 已废弃

3.2. 3个基本操作

3.2.1. 回收新生代(同时暂停所有的应用程序线程)

3.2.2. 运行并发周期来清理老年代的数据

3.2.3. 如果有必要,执行Full GC来压缩老年代

3.3. 对象会从Eden空间移到一个Survivor空间(Survivor空间满了之后就移动到老年代)

3.4. 默认情况下,CMS不会回收元空间

3.5. 除非发生Full GC,否则永远不会调整新生代的大小

3.6. CMS周期的停顿一般比新生代停顿短得多,特定的应用程序可能对这些额外的停顿并不敏感

3.7. 并发周期

3.7.1. 初始标记(initial mark)阶段开始

  • 3.7.1.1. 会暂停所有应用程序线程

3.7.2. 标记(mark)阶段

  • 3.7.2.1. 不会暂停应用程序线程

3.7.3. 预清理(preclean)阶段

  • 3.7.3.1. 和应用程序线程同时运行

3.7.4. 重新标记(remark)阶段

  • 3.7.4.1. 不是并发的,会暂停所有的应用程序线程

3.7.5. 可中止的预清理(abortable preclean)阶段

  • 3.7.5.1. 会等到新生代被占用50%后才开始

  • 3.7.5.2. 阶段的中止,这是停止这个阶段的唯一方式

3.7.6. 清除(sweep)阶段

3.7.7. 并发重置(reset)阶段

  • 3.7.7.1. 最后一个阶段

3.8. 当发生新生代回收,但是老年代中并没有足够的空间容纳预计要晋升的对象时,CMS会执行Full GC

3.8.1. 所有的应用程序线程都会被暂停

3.8.2. 该操作是单线程的

  • 3.8.2.1. 它耗时如此之长的原因之一

  • 3.8.2.2. 并发模式失败随着堆的增长而影响更大的原因之一

3.8.3. 这种并发模式失败是CMS被废弃的主要原因

3.9. 老年代中有足够的空间容纳晋升对象,但空闲空间是碎片化的,所以对象晋升还是会失败

3.9.1. CMS无法晋升对象,因为老年代是碎片化的

3.9.2. 要晋升的内存量比CMS预期的要大,这种情况更少

3.10. CMS的日志可能显示了Full GC,但是没有任何常规的并发GC消息

3.10.1. 当元空间被填满并需要回收时,这种情况就会发生

3.10.2. CMS不会回收元空间,所以如果它被填满了,就需要Full GC来丢弃任何未被引用的类

3.11. 优化

3.11.1. 确保不会发生并发模式失败或晋升失败

  • 3.11.1.1. 避免并发模式失败是实现CMS最佳性能的关键

  • 3.11.1.2. 避免这些失败的最简单的方法(在可能的情况下)是增加堆的大小

3.11.2. 当执行新生代回收时,CMS计算出没有足够的空间容纳这些将晋升到老年代的对象,所以会先回收老年代

3.11.3. 让老年代空间更大一些,要么调整新生代和老年代的比例,要么增加更多的堆空间

3.11.4. 更频繁地运行后台线程

  • 3.11.4.1. 提早开始并发周期

  • 3.11.4.2. -XX:CMSInitiatingOccupancyFraction=N

    • 3.11.4.2.1. 默认为70

    3.11.4.2.1.1. CMS并发周期会在老年代被占用70%时开始

    • 3.11.4.2.2. 不要将该值设置得低于堆中的活跃数据量,至少应加上10%到20%
  • 3.11.4.3. -XX:+UseCMSInitiatingOccupancyOnly

    • 3.11.4.3.1. 默认情况下false
  • 3.11.4.4. 如果两个都设置了,CMS就会只根据老年代的填充比例来决定何时启动后台线程

  • 3.11.4.5. 此处的占用率是基于老年代的,而不是整个堆

3.11.5. 使用更多的后台线程

  • 3.11.5.1. -XX:ConcGCThreads=N

    • 3.11.5.1.1. 增加后台线程数量

    • 3.11.5.1.2. ConcGCThreads = (3 + ParallelGCThreads) / 4

    • 3.11.5.1.3. 能比G1 GC早一步增加ConcGCThreads的值

3.12. MaxGCPauseMllis=N

3.12.1. 见前文

3.13. GCTimeRatio=N

3.13.1. 见前文

posted @ 2023-03-16 06:35  躺柒  阅读(82)  评论(2编辑  收藏  举报