读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的值
-