Solaris 10:虛擬記憶體耗盡
我們的團隊都是程序員,專門使用 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
原來是 gmake 3.81 中的一個錯誤。當我不使用 make 直接執行編譯命令時,它能夠使用更多的記憶體。3.80 中似乎有一個已知的錯誤:類似這樣的東西。該錯誤應該在 3.81 中修復。但我遇到了一個非常相似的錯誤。
所以我嘗試了gmake 3.82。編譯繼續進行,我再也沒有看到 VM 錯誤。
我永遠無法讓它轉儲核心,所以我實際上不知道虛擬記憶體、gmake、g++ 或 as 用完了什麼。它只是不會在該錯誤上轉儲核心。我也不知道這個錯誤到底是什麼,但它現在似乎正在工作。
我有幾件事要你試試:
- 我認為@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
嗎?如果兩者都崩潰,它生成的核心轉儲大小是否大致相同?
coredump 大小應該表明程序在崩潰之前獲得了多少記憶體。將其與系統限制進行比較:
- 在
top
我正在查看的 Solaris 機器上顯示 511G 物理記憶體,153G 空閒,20G 總交換,20G 空閒交換。- 執行
swap -l
和比較更改交換大小後,行為是否會以任何明顯的方式發生變化?
- 更快崩潰/需要更長的時間才能崩潰
- 核心尺寸不同
嘗試有意使用較小的交換大小(或完全擺脫交換分區)以查看它是否更快崩潰、產生更小的核心轉儲或以任何其他方式改變行為。
放入一個大硬碟驅動器並使用整個驅動器進行交換。
此外,查看一些 GCC 標誌,例如:
-Q
在編譯時列印函式名稱和每次傳遞的統計資訊-ftime-report
每次通過的時間資訊- 各種
-fdump-rtl*
旗幟- 嘗試在不同階段(預處理器、彙編等)獲取輸出,看看是否有不同的行為