title: 遇见pod重启 author: Gamehu date: 2024-02-18 09:31:29 tags: ---
遇见系列 第五篇
现场的应用A pod一段时间就会重启,用k8s命令看pod是因为超出limits。 查看了gc日志。因为使用的是G1回收器 让我详细解释G1 GC的工作流程: 内存分区: 整个堆内存被划分为大小相等的Region(默认2048个) Region大小是2的幂次方,范围从1MB到32MB 每个Region可以是Eden、Survivor、Old或Humongous区 Humongous区专门用于存储大于Region大小一半的对象 GC周期的四个主要阶段: a) 初始标记(Initial Mark)- STW: 标记所有直接从GC Roots可达的对象 这个阶段需要Stop-The-World(STW) 通常和年轻代收集同时进行 时间很短 b) 并发标记(Concurrent Mark): 遍历整个堆的对象图 标记所有可达的对象 与应用程序并发执行 使用SATB(Snapshot-At-The-Beginning)算法 记录标记期间的引用变化 c) 最终标记(Final Mark)- STW: 处理并发标记阶段遗留的SATB缓冲区 完成最终的标记工作 需要STW,但时间较短 d) 筛选回收(Live Data Counting and Evacuation)- STW: 计算各个Region的垃圾比例 根据用户期望的停顿时间建立回收集合 优先回收价值最大的Region(垃圾最多) 将存活对象复制到新的Region 清空旧Region 特殊处理: a) 年轻代收集: 当Eden区满时触发 只关注年轻代Region 存活对象复制到Survivor区或老年代 b) 混合收集: 同时回收年轻代和部分老年代Region 根据全局并发标记的结果 老年代Region按垃圾收集价值排序 优化机制: a) Remembered Sets(Rsets): 记录跨Region的引用关系 每个Region都有一个RSet 避免全堆扫描 b) Collection Sets(CSets): 需要被回收的Region集合 基于垃圾占比和期望停顿时间选择 正常GC流程: Young GC:回收年轻代Region 并发标记周期:标记整个堆的存活对象 混合回收:回收年轻代和部分老年代Region Full GC触发条件: 并发标记完成之前就出现内存分配失败 回收后的空间仍不足以支持下一次Young GC(空间担保失败) 混合回收后仍无法满足内存需求 Humongous对象分配失败 Full GC的特点: 必须Stop-The-World(STW) 对整个堆进行压缩和整理 单线程执行,停顿时间长 会明显影响应用程序性能 Full GC后的处理: 如果Full GC后内存足够,应用继续运行 如果Full GC后内存仍然不足,抛出OutOfMemoryError 避免Full GC的建议: 合理设置堆内存大小 调整并发标记的触发阈值 适当调整Region大小 控制大对象的产生 及时进行并发标记,避免内存耗尽 Full GC在G1中的具体执行步骤: 1. 标记阶段(Marking Phase): - 进入STW阶段,停止所有用户线程 - 从GC Roots开始进行全堆标记 - 标记所有存活对象 - 使用单线程进行标记,效率较低 2. 清理阶段(Cleanup Phase): - 清理所有未被标记的对象(垃圾对象) - 回收空的Region - 重置各种元数据信息(RSet等) - 计算所有Region的存活对象信息 3. 压缩阶段(Compaction Phase): - 对存活对象进行压缩整理 - 将存活对象复制到新的Region - 按照新的分代布局重新整理对象 - 更新对象引用关系 - 释放旧的Region - 重建RSet等辅助数据结构 特点: - 整个过程完全STW - 单线程执行,性能较差 - 会对整个堆内存进行处理 - 会导致较长时间的停顿 - 会破坏原有的分代结构,需要重新构建 触发Full GC通常意味着: - 内存分配压力过大 - 并发收集来不及回收内存 - 可能需要调整GC参数或应用程序代码