我们的团队都是程序员,专门使用 Linux 或 MacOS,但客户使用 Solaris 10,我们需要我们的代码在那里工作。因此,我们找到了一台旧的 SunFire V240 和一台租用的 Solaris 10 VM 进行测试。
该代码在 VM 上编译得很好,但在 SunFire 上却失败了。我们的代码有一个巨大的自动生成的 C++ 文件作为构建的一部分。就是这个巨大的文件无法编译。它失败并显示消息:virtual memory exhausted: Not enough space
我想不通。 SunFire有8GB RAM,当编译达到1.2GB多一点时,就会出现虚拟内存耗尽的情况。没有其他重要的事情正在运行。以下是一些接近失败的内存统计数据:
使用prstat -s size
:
SIZE (virtual memory): 1245 MB
RSS (real memory): 1200 MB
根据echo "::memstat" | mdb -k
,大量内存仍然可用:
Free (cachelist) is 46%
Free (freelist) is 26% of total.
在编译失败之前,所有用户进程都使用了大约 17% 的 RAM。 (发生故障后,用户 RAM 使用量下降至 2%。)这与其他 RAM 使用量数字一致。 (1.2GB /8.0GB ~= 15%)
swap -l
报告说交换完全未使用。
其他一些细节:
我们使用 g++ 6.1.0 进行构建,编译为 64 位。无论我们是否将 -m64 标志传递给编译器,它都会失败。
# uname -a
SunOS servername 5.10 Generic_147440-27 sun4u sparc SUNW,Sun-Fire-V240
VM 和 SunFire 的系统限制设置如下:
>ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
open files (-n) 256
pipe size (512 bytes, -p) 10
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 29995
virtual memory (kbytes, -v) unlimited
(using su)>rctladm -l
...
process.max-address-space syslog=off [ lowerable deny no-signal bytes ]
process.max-file-descriptor syslog=off [ lowerable deny count ]
process.max-core-size syslog=off [ lowerable deny no-signal bytes ]
process.max-stack-size syslog=off [ lowerable deny no-signal bytes ]
process.max-data-size syslog=off [ lowerable deny no-signal bytes ]
process.max-file-size syslog=off [ lowerable deny file-size bytes ]
process.max-cpu-time syslog=off [ lowerable no-deny cpu-time inf seconds ]
...
我们尝试将堆栈大小设置为“无限制”,但这不会产生任何可识别的差异。
# df
/ (/dev/dsk/c1t0d0s0 ):86262876 blocks 7819495 files
/devices (/devices ): 0 blocks 0 files
/system/contract (ctfs ): 0 blocks 2147483608 files
/proc (proc ): 0 blocks 29937 files
/etc/mnttab (mnttab ): 0 blocks 0 files
/etc/svc/volatile (swap ):14661104 blocks 1180179 files
/system/object (objfs ): 0 blocks 2147483465 files
/etc/dfs/sharetab (sharefs ): 0 blocks 2147483646 files
/platform/sun4u-us3/lib/libc_psr.so.1(/platform/sun4u-us3/lib/libc_psr/libc_psr_hwcap1.so.1):86262876 blocks 7819495 files
/platform/sun4u-us3/lib/sparcv9/libc_psr.so.1(/platform/sun4u-us3/lib/sparcv9/libc_psr/libc_psr_hwcap1.so.1):86262876 blocks 7819495 files
/dev/fd (fd ): 0 blocks 0 files
/tmp (swap ):14661104 blocks 1180179 files
/var/run (swap ):14661104 blocks 1180179 files
/home (/dev/dsk/c1t1d0s0 ):110125666 blocks 8388083 files
编辑 1:设置 16GB 交换文件后的交换输出:
注:块大小为512
# swap -l
swapfile dev swaplo blocks free
/dev/dsk/c1t0d0s1 32,25 16 2106416 2106416
/home/me/tmp/swapfile - 16 32964592 32964592
# swap -s
total: 172096k bytes allocated + 52576k reserved = 224672k used, 23875344k available
答案1
我有几件事供你尝试:
- 我认为@AndrewHenle 是正确的你需要更多交换空间。
- 您可以尝试摆弄 GCC 的模板和 constexpr 递归深度限制(
-ftemplate-depth
和-fconstexpr-depth
)。不过,我希望它最多只能帮助您了解哪些表达式导致内存不足。 - 一些调试技巧(更多内容见下文)
本文详细介绍了如何增加 Solaris 中的交换空间,但是总有一天该链接会被破坏,因此以下是该文章的摘要:
# Identify the current swap volume.
$ swap -l
swapfile dev swaplo blocks free
/dev/zvol/dsk/rpool/swap 256,1 16 1058800 1058800
# Do one of the following:
# a) Modify the existing swap volume (REQUIRES REBOOT)
$ zfs get volsize rpool/swap
NAME PROPERTY VALUE SOURCE
rpool/swap volsize 517M -
$ zfs set volsize=2g rpool/swap
$ zfs get volsize rpool/swap
NAME PROPERTY VALUE SOURCE
rpool/swap volsize 2G -
$ init 6
# b) Add an additional swap volume
# Create it
$ zfs create -V 2G rpool/swap2
# Activate it
$ swap -a /dev/zvol/dsk/rpool/swap2
$ swap -l
swapfile dev swaplo blocks free
/dev/zvol/dsk/rpool/swap 256,1 16 1058800 1058800
/dev/zvol/dsk/rpool/swap2 256,3 16 4194288 4194288
# Add an entry for the new volume in /etc/vfstab
$ /opt/csw/gnu/grep -P '\sswap' /etc/vfstab
/dev/zvol/dsk/rpool/swap - - swap - no -
/dev/zvol/dsk/rpool/swap2 - - swap - no -
如果您想尝试诊断问题,可以尝试以下一些操作:
# This tells Solaris to add all available sections into coredumps &
# place coredumps in your home directory with the given pattern
$ coreadm -p ~/%t.%n.%u.%f.%p.core -P all
$ gcc ${flags} source.cc -fsyntax-only
$ gcc ${flags} source.cc -c -o source.o
寻找/尝试的事情:
- 它会崩溃吗
-fsyntax-only
? - 如果两者都崩溃,它生成的核心转储大小是否大致相同?
- 核心转储大小应该表明进程在崩溃之前获取了多少内存。将其与系统限制进行比较:
- 在
top
Solaris 机器中,我看到显示 511G 物理内存、153G 可用内存、20G 总交换空间、20G 可用交换空间。 - 运行
swap -l
并比较
- 在
- 更改交换大小后,行为是否会发生任何明显的变化?
- 崩溃得更快/崩溃的时间更长
- 核心尺寸不同
- 尝试有意使用较小的交换大小(或完全删除交换分区),看看它是否会更快崩溃、产生更小的核心转储或以任何其他方式改变行为。
- 放入一个大硬盘并使用整个驱动器进行交换。
此外,查看一些 GCC 标志,例如:
-Q
打印编译时的函数名称以及每次传递的统计信息-ftime-report
每次传递的时间信息- 各种
-fdump-rtl*
旗帜 - 尝试在不同阶段(预处理器、汇编等)获取输出,看看是否会得到不同的行为
答案2
事实证明这是 gmake 3.81 中的一个错误。当我不使用 make 直接运行编译命令时,它能够使用更多的内存。 3.80 中似乎有一个已知的错误:像这样的东西。该错误应该在 3.81 中得到修复。但我遇到了一个非常类似的错误。
所以我尝试了gmake 3.82。编译继续进行,我没有再看到虚拟机错误。
我从来没能让它转储核心,所以我实际上不知道虚拟内存、gmake、g++ 或 as 耗尽了什么。它只是不会因该错误而转储核心。我也不知道这个错误到底是什么,但它现在似乎正在发挥作用。