由于频繁的垃圾收集导致服务吞吐量低
Posted
技术标签:
【中文标题】由于频繁的垃圾收集导致服务吞吐量低【英文标题】:Low throughput of service due to frequent Garbage Collection 【发布时间】:2019-09-19 15:08:40 【问题描述】:我有一个服务在 16GB RAM 的系统上运行,配置如下:
-Xms6144M"
-Xmx6144M
-XX:+UseG1GC
-XX:NewSize=1500M
-XX:NewSize=1800M
-XX:MaxNewSize=2100M
-XX:NewRatio=2
-XX:SurvivorRatio=12
-XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis=1000
-XX:GCTimeRatio=9
-XX:-UseAdaptiveSizePolicy
-XX:+PrintAdaptiveSizePolicy
它有大约 20 个轮询器,每个轮询器都有大小为 30 的 ThreadPoolExecutor 来处理消息。最初大约 5-6 小时,它每秒能够处理大约 130 条消息。此后,它每秒只能处理大约 40 条消息。
我分析了 GC 日志,发现 Full GC 变得非常频繁,超过 1000MB 的数据正在从年轻代提升到老年代:
查看堆转储,我看到许多处于等待状态的线程类似于:WAITING at sun.misc.Unsafe.park(Native Method) 并且以下类对象获得最多保留大小:
我认为服务及其相关库中可能存在少量内存泄漏,这些泄漏会随着时间的推移而累积,因此增加堆大小只会推迟这一点。或者可能是因为 Full GC 变得非常频繁,所有其他线程都非常频繁地停止(“停止世界”暂停)。需要帮助找出这种行为的根本原因。
【问题讨论】:
“它有大约 20 个轮询器运行,每个轮询器都有大小为 30 的 ThreadPoolExecutor 用于处理消息。” - 这似乎太过分了。 我认为你是对的。这看起来像内存泄漏。这导致 GC 必须越来越频繁地运行……直到您花费大部分时间运行 stop-the-world GC。 GC 调整无济于事。查找并修复内存泄漏。 泄漏看起来可能与最终确定有关。调查!!正如各种消息来源所说,最终化发生在主 GC 运行之后,并且通常是单线程的。如果要完成的最终确定太多,则可能成为瓶颈。Reference
对象也会发生同样的事情。如果这是问题所在,那么 1) 查看是否可以启用多个终结器/引用队列处理线程,以及 2) 查看是否可以减少对终结和 Reference
对象的使用。两者都很贵。
【参考方案1】:
GC 模式看起来像内存泄漏。
查看您的堆转储统计信息,我可以看到 3M 任务在线程池中等待执行。
我可以推测,您正在使用具有无限任务队列的线程池。您的消息入站率大于系统的处理能力,因此积压越来越多,消耗更多内存最终导致 GC 死亡。
根据您的情况,您可以限制线程池的队列大小或尝试优化队列任务的内存占用。
限制队列大小会对前一个处理阶段产生背压。如果是简单的定时器驱动轮询器作为线程池的生产者,效果将是减少轮询间隔(因为轮询器会阻塞等待队列中的空间)。
任务内存占用的优化只有在您的平均处理能力高于入站任务速率并且问题是由临时激增引起的情况下才会起作用。
【讨论】:
以上是关于由于频繁的垃圾收集导致服务吞吐量低的主要内容,如果未能解决你的问题,请参考以下文章