Solaris

Solaris 10:虛擬記憶體耗盡

  • May 13, 2019

我們的團隊都是程序員,專門使用 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 用完了什麼。它只是不會在該錯誤上轉儲核心。我也不知道這個錯誤到底是什麼,但它現在似乎正在工作。

我有幾件事要你試試:

  1. 我認為@AndrewHenle 是正確的,您需要更多交換空間
  2. 您可以嘗試擺弄 GCC 的模板和 constexpr 遞歸深度限制(-ftemplate-depth-fconstexpr-depth)。但充其量,我希望它只會幫助您了解哪些表達式導致它耗盡記憶體。
  3. 一些調試技巧(更多內容見下文)

本文詳細介紹瞭如何在 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*旗幟
  • 嘗試在不同階段(預處理器、彙編等)獲取輸出,看看是否有不同的行為

引用自:https://unix.stackexchange.com/questions/516833