可能存在 Linux 页表问题/JVM 堆内存过大导致平均负载过大,从而导致 GC 日志中的系统时间过长

可能存在 Linux 页表问题/JVM 堆内存过大导致平均负载过大,从而导致 GC 日志中的系统时间过长

我们的服务在 AWS 的 m5.12xlarge 节点(48 核,192 G RAM)上运行,该节点使用 Ubuntu 16.04。我们使用 Java 8。对于我们的服务,我们分配了大约 150G 作为最大堆大小。我们在节点上没有交换。我们的服务的性质是它分配了大量的大型短期对象。除此之外,通过我们依赖的第三方库,我们创建了许多短期进程,这些进程通过管道与我们的进程通信,并在处理少量请求后被回收。

我们注意到,在进程启动后,进程的 RES(位于 top)达到约 70G 时,CPU 中断显著增加,JVM 的 GC 日志显示系统时间猛增至数十秒(有时为 70 秒)。在此状态下,这 48 个核心节点上的平均负载最初小于 1,最后几乎达到 10。

sar 输出表明,当节点处于此状态时,最小页面错误会显著增加。总体而言,大量 CPU 中断与此状态相关。

重启服务只能暂时缓解压力。平均负载缓慢但稳步上升,GC 系统时间再次飙升。

我们在大约 10 个节点的集群上运行我们的服务,每个节点的负载(几乎)均匀分布。我们发现有些节点比其他正常工作的节点更频繁、更快地进入此状态。

我们尝试了各种 GC 选项以及诸如大页面/THP 等选项,但都没有成功。

以下是 loadavg 和 meminfo 的快照

    /proc/meminfo on a node with high load avg:

    MemTotal:       193834132 kB
    MemFree:        21391860 kB
    MemAvailable:   52217676 kB
    Buffers:          221760 kB
    Cached:          9983452 kB
    SwapCached:            0 kB
    Active:         144240208 kB
    Inactive:        4235732 kB
    Active(anon):   138274336 kB
    Inactive(anon):    24772 kB
    Active(file):    5965872 kB
    Inactive(file):  4210960 kB
    Unevictable:        3652 kB
    Mlocked:            3652 kB
    SwapTotal:             0 kB
    SwapFree:              0 kB
    Dirty:             89140 kB
    Writeback:             4 kB
    AnonPages:      138292556 kB
    Mapped:           185656 kB
    Shmem:             25480 kB
    Slab:           22590684 kB
    SReclaimable:   21680388 kB
    SUnreclaim:       910296 kB
    KernelStack:       56832 kB
    PageTables:       611304 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:    96917064 kB
    Committed_AS:   436086620 kB
    VmallocTotal:   34359738367 kB
    VmallocUsed:           0 kB
    VmallocChunk:          0 kB
    HardwareCorrupted:     0 kB
    AnonHugePages:  85121024 kB
    CmaTotal:              0 kB
    CmaFree:               0 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    DirectMap4k:      212960 kB
    DirectMap2M:    33210368 kB
    DirectMap1G:    163577856 kB



/proc/meminfo on a node that is behaving ok
    MemTotal:       193834132 kB
    MemFree:        22509496 kB
    MemAvailable:   45958676 kB
    Buffers:          179576 kB
    Cached:          6958204 kB
    SwapCached:            0 kB
    Active:         150349632 kB
    Inactive:        2268852 kB
    Active(anon):   145485744 kB
    Inactive(anon):     8384 kB
    Active(file):    4863888 kB
    Inactive(file):  2260468 kB
    Unevictable:        3652 kB
    Mlocked:            3652 kB
    SwapTotal:             0 kB
    SwapFree:              0 kB
    Dirty:           1519448 kB
    Writeback:             0 kB
    AnonPages:      145564840 kB
    Mapped:           172080 kB
    Shmem:              9056 kB
    Slab:           17642908 kB
    SReclaimable:   17356228 kB
    SUnreclaim:       286680 kB
    KernelStack:       52944 kB
    PageTables:       302344 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:    96917064 kB
    Committed_AS:   148479160 kB
    VmallocTotal:   34359738367 kB
    VmallocUsed:           0 kB
    VmallocChunk:          0 kB
    HardwareCorrupted:     0 kB
    AnonHugePages:  142260224 kB
    CmaTotal:              0 kB
    CmaFree:               0 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    DirectMap4k:      149472 kB
    DirectMap2M:    20690944 kB
    DirectMap1G:    176160768 kB

火焰图中最重要的部分是:

https://i.stack.imgur.com/yXmOM.png

偶然间,我们重启了一个节点,发现它以非常稳定的方式运行了大约 2 周,其他地方没有任何变化。从那时起,我们不得不重启处于这种状态的节点以获得一些喘息空间。后来我们在其他地方发现这些症状可能与页表被卡住有关,只有通过重启才能缓解。目前还不清楚这是否正确,也不清楚这是否是我们遇到这种情况的原因。

有没有办法可以永久解决这个问题?

答案1

透明大页面正在变得碎片化或混乱。在 Linux 上,这是考虑放弃透明并明确设置页面大小的内存大小。

差值大于 10 GB(坏值减去好值):

Committed_AS:   274.3
AnonHugePages:  -54.5
DirectMap2M:    11.9
DirectMap1G:    -12.0

DirectMap 从 1G 变为 2M 表明 x86 TLB 和 Linux 内部可用的连续空间更少。其中 AnonHugePages 丢失了 50 GB,这是一个很大的差异。不知何故,这导致 Committed_AS 膨胀到 MemTotal 的 225%,这是一个不好的症状;这个系统将疯狂地进行页面调出。

鉴于火焰图堆栈中的页面错误,Linux 虚拟内存系统对页面进行重新排列会产生大量开销。


提高性能包括明确配置大页面。150 GB 的堆远远超出了 30 GB 的过渡点,在该过渡点上压缩指针不再可行。(关于保持在此 30 GB 阈值以下的文章很多。)三位数 GB 也是我认为需要认真评估 Linux 大页面的大小。

在 OpenJDK 或 Oracle JDK 上:首先正确分配大页面,然后使用选项 -XX:+UseLargePagesJava 对大内存页面的支持HugePages 上的 Debian wiki

如果你还想尝试垃圾收集器,请查看OpenJDK 的 ZGC 维基页面。有限的暂停时间、处理大堆和 NUMA 意识是明确的目标。简而言之,还要添加实验选项: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+UseLargePages。ZGC wiki 页面还讨论了使用 Linux 大页面池和 hugetlbfs 的麻烦,有这些事情的例子总是有帮助的。


关于 NUMA,想一想这将在哪个 CPU 上运行。可能是两个 24 核插槽左右。AWS 没有具体说明,但说它是一个白金 8175。由于您将在不同的套接字上执行,因此部分内存将不位于套接字本地。即使虚拟机管理程序不向 VM 客户机公开此拓扑,情况也是如此。

然而,现代 Xeon 上的两个插槽可以产生可控的 NUMA 效果。页面大小是一个更大的问题。

相关内容