Linuxのスワップ領域(swap)は、RAMが足りなくなったときに、しばらく使われていないメモリの中身をディスクへ退避させる領域です。この記事では、スワップを確認するコマンド、RAM容量ごとの推奨サイズ、スワップファイルの作成と削除、vm.swappiness の設定までを、実際に打ったコマンドと出力で説明します。
特に注意してほしいのが vm.swappiness=0 です。Ubuntu 24.04(カーネル6.8)の検証VMで計測すると、空きスワップが1GiBあってもスワップを1KBも使わず、プロセスがOOM Killされました。「0はスワップを最小限にする設定」という説明は、現在の多くのカーネルでは当てはまりません。
まとめ:Linuxスワップ運用の要点
- 確認:量は
free -hとswapon --show、発生しているかはvmstat 1のsi・so列、プロセス別は/proc/[pid]/statusのVmSwap。 - サイズ:RHEL 9はRAM 2〜8GBで同量、8〜64GBで最低4GB。AWSは2GiB以下でRAMの2倍(最低32MB)、2GiB超・64GiB未満でRAMの0.5倍。どちらも最終的にはワークロードで決める。
- 作成:
fallocate→chmod 600→mkswap→swapon→/etc/fstab。mkswap --fileはutil-linux 2.40以降でないと使えない。 - vm.swappiness:既定60、範囲0〜200。下げる場合も0は使わない(DBなら1)。MGLRUが有効なカーネルでは0がスワップの無効化と同じ結果になる。
- コンテナ:cgroupのメモリ上限に達したときも、0だとスワップせずにOOM Killされる。Kubernetesのノードswapは1.34で安定版になったが、既定はNoSwap。
スワップ領域の仕組み|匿名ページの退避とページ回収
メモリが足りなくなると、カーネルは空きを作るためにページ(メモリの管理単位)を回収します。回収の対象は2種類あります。ディスク上の内容と一致するクリーンなページキャッシュは、破棄しても後でファイルから読み直せます。変更済みのダーティページは、原則として書き戻してから回収します。プログラムが確保した匿名ページ(ファイルの裏付けがないメモリ)は捨てられないので、スワップ領域に書き出してから空けます。この書き出しがスワップアウト、読み戻しがスワップインです。
つまりスワップが無いと、通常の匿名ページをスワップへ退避できず、ページキャッシュや回収可能なカーネル内キャッシュを解放しても割り当てに必要なメモリを確保できなければ、OOM Killer(Out Of Memory Killer)がプロセスを強制終了します。ページキャッシュの仕組みはディスクキャッシュとは?仕組み・メモリキャッシュとの違いとLinux・Windowsでの確認方法で詳しく扱っています。
バックグラウンドでの回収は kswapd というカーネルスレッドが担います。メモリ割り当てを要求した処理が同期的に回収するdirect reclaimもあります。カーネルの文書によると、空きページが下限(low watermark)まで減るとkswapdが起こされ、非同期にページを回収します。topで kswapd0 のCPU使用率が高止まりしているときは、メモリが足りず回収が続いている状態です。スワップはRAMより桁違いに遅いため、恒常的に使われているならRAM不足を疑います。ページ単位の退避とプロセス単位の退避の用語の違いはスワッピングとは?ページングとの違いとスラッシングの判定手順を実装目線で解説で整理しています。
Linuxでスワップを確認するコマンド|free・swapon・vmstat
以下の出力は、検証用VM(Ubuntu 24.04.4 LTS、カーネル6.8.0-117-generic、メモリ約2GiB)に1GiBのスワップファイルを作った状態で取得したものです。
free・swaponによる容量・使用量・優先度の確認
$ free -h
total used free shared buff/cache available
Mem: 1.9Gi 333Mi 1.3Gi 724Ki 447Mi 1.6Gi
Swap: 1.0Gi 0B 1.0Gi
$ swapon --show
NAME TYPE SIZE USED PRIO
/swapfile file 1024M 0B -2
$ cat /proc/swaps
Filename Type Size Used Priority
/swapfile file 1048572 0 -2
free -h のSwap行は左から合計・使用量・空きです。swapon --show は既定でNAME・TYPE・SIZE・USED・PRIOの5列を出し、スワップ領域が無ければ何も表示しません。PRIOは大きいほど先に使われ、同じ値の領域どうしは交互に使われます。swapon -s は /proc/swaps と同じ形式で、swaponのマニュアルで非推奨(--show を使う)とされています。
vmstatのsi・soによるスワップI/Oの確認
使用量が0でないこと自体は問題ではありません。見るべきは、いまスワップの出し入れが続いているかです。vmstat 1 のsiが1秒あたりのスワップイン、soがスワップアウトの量(KiB)です。次は、128MiBのメモリ上限をかけたサービスに300MiBを確保させている間の出力です。
$ vmstat 1 7
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
r b swpd free buff cache si so bi bo in cs us sy id wa st gu
0 0 0 1786028 1248 83516 644 10938 2676 11109 880 3 2 5 92 1 0 0
0 0 0 1786028 1248 83592 0 0 0 0 60 97 0 1 100 0 0 0
0 0 0 1786028 1248 83592 0 0 0 0 38 72 0 0 100 0 0 0
0 0 180512 1654484 1252 83596 336 180292 2720 180292 1709 1043 4 23 73 0 0 0
0 0 180512 1654492 1252 83604 0 0 0 0 71 115 0 0 100 0 0 0
0 0 56 1697532 1252 83560 2464 3048 2464 3048 500 807 1 3 97 1 0 0
1行目は起動してからの平均値なので、判断には使いません。4行目ではso=180292KiB毎秒(約176MiB毎秒)となり、swpd(使用中のスワップ)は0から180512KiBへ増えました。その後はsi・soとも0に戻り、プロセスが終わった6行目でswpdがほぼ0になっています。一度で終わる退避でも、処理の遅延が許容範囲かどうかを確認して影響を判断します。siとsoが継続して非ゼロならスワップI/Oが続いていますが、同じページを繰り返し出し入れしているとは限りません。応答時間やメモリ・I/OのPSIも確認し、処理の停滞を伴う場合にスラッシングを疑います。
VmSwapによるプロセス別スワップ使用量の比較
freeやvmstatでは、どのプロセスが退避されているかは分かりません。プロセスごとの量は /proc/[pid]/status のVmSwap行で確認できます。ただしVmSwapは匿名プライベートデータの退避量で、共有メモリのスワップ使用量は含みません。追加パッケージなしで多い順に並べるなら、次の1行で足ります。
$ grep VmSwap /proc/*/status 2>/dev/null | sort -k2 -nr | head -3
/proc/2060/status:VmSwap: 180140 kB
/proc/thread-self/status:VmSwap: 0 kB
/proc/self/status:VmSwap: 0 kB
PID 2060が約176MiBを退避していると分かります。プロセス名まで一緒に見たいなら smem -rs swap が使えます(多くのディストリビューションで別途インストールが必要)。systemdのサービス単位で見るなら、cgroupの memory.swap.current がサービス全体の退避量です(同じ検証で184,709,120バイト)。
スワップサイズの推奨値|RHEL 9とAWSのガイドライン
「RAMの2倍」はRAMが小さかった時代の目安です。RHEL 9のドキュメントはRAMが増えるほど比率を下げる表を載せており、RHEL 10も同じ値です。
| RAM(RHEL 9の区分) | 推奨スワップ | ハイバネートを使う場合 |
|---|---|---|
| 2GiB未満 | RAMの2倍 | RAMの3倍 |
| 2〜8GB | RAMと同量 | RAMの2倍 |
| 8〜64GB | 最低4GB | RAMの1.5倍 |
| 64GiB超 | ワークロード次第(最低4GiB) | 非推奨 |
EC2向けには、AWSがre:Postのナレッジセンターで別の表を出しています。区分の境界がRHELと違うので、表を分けて示します。
| RAM(AWSの区分) | 推奨スワップ |
|---|---|
| 2GiB以下 | RAMの2倍(32MB未満にはしない) |
| 2GiB超・64GiB未満 | RAMの0.5倍 |
| 64GiB超 | ワークロード・用途次第 |
| 64GiBちょうど | 原表に明示なし |
たとえばRAM 4GiBなら、RHELの同量という目安では4GiB、AWSの0.5倍という目安では2GiBとなります。どちらを使っても、Red Hat自身が書いているとおり、推奨量は「メモリ容量ではなくメモリのワークロードで決まる」ものです。
利用するOSやクラウドの推奨値を出発点に、ピーク時のメモリ需要と応答時間で調整します。スワップ容量の追加は退避先の不足への対策であり、soが繰り返し出るだけで増量を決めるものではありません。ハイバネート(休止状態)では、通常のスワップ使用分に加えて休止イメージを保存する空き容量が必要です。必要量は使用中のメモリやイメージの圧縮・サイズ設定にも左右されるため、利用するディストリビューションの推奨値に従って確保します。
スワップファイルを作成・削除する手順
fallocate・mkswap・swaponによる作成とfstabへの登録
後からサイズを変えやすいスワップファイルが、クラウドや開発環境では第一候補です。検証VMで1GiBを作った手順です。
$ sudo fallocate -l 1G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=58043741-b2a5-4730-ab9b-1e87e399227e
$ sudo swapon /swapfile
# 再起動後も有効にするため /etc/fstab に追記する
/swapfile none swap sw 0 0
ArchWikiなどには mkswap --size 4G --file /swapfile の1行で作る方法が載っていますが、Ubuntu 24.04のutil-linux 2.39.3では unrecognized option '--size' で失敗しました。--file はutil-linux 2.40のリリースノートで追加されたオプションです。mkswap --version で版を確かめてから使ってください。
ファイルシステムによっては、fallocateで確保したファイルでも「穴がある」と扱われ、swaponが拒否します。その場合は dd if=/dev/zero of=/swapfile bs=1M count=1024 で作り直します(AWSのEC2向け手順もddを使っています)。Btrfsでは通常の手順が使えず、Btrfsの公式手順どおり btrfs filesystem mkswapfile --size 2G swapfile で作ります。
パーティション・LVMとの選び分け
| 方式 | サイズ変更 | 向くケース |
|---|---|---|
| スワップファイル | 容易 | クラウド・開発機・後から足す |
| スワップパーティション | 再パーティションが必要 | OSインストール時に固定で確保 |
| LVM論理ボリューム | 停止・LV拡張・再初期化が必要 | 既にLVMで組んでいるサーバー |
後から足すならファイル、インストーラーで最初から作るならパーティションかLVM、と作る時期で選べば十分です。
スワップの無効化・削除・クリアの手順
スワップファイルを消すときは、無効化してから削除し、fstabの行も消します。溜まったスワップを空にしたい場合は、対象領域を無効化して再有効化します。以下の一括コマンドは、再有効化する全領域がfstabに登録され、noautoが付いていない場合に限ります。手動で有効化した領域は、対象を明示して再有効化してください。
# 削除
$ sudo swapoff /swapfile
$ sudo rm /swapfile
# /etc/fstab の /swapfile の行を削除する
# クリア(退避中のページをRAMへ読み戻してから再有効化)
$ sudo swapoff -a && sudo swapon -a
swapoffでは退避したページを読み戻すためのメモリが必要ですが、成否はホストの空きRAMとスワップ使用量の比較だけでは決まりません。cgroupのメモリ上限がある場合は、ホストに空きがあっても読み戻しで上限に達し、OOM Killが起きることがあります。検証では、128MiB上限のサービスが約176MiBを退避した状態でswapoffを打つと、swapoff failed: Cannot allocate memory で失敗し、同時にそのサービスがOOM Killされました。クリアの前に、free -h のavailableがSwapのusedより十分大きいことと、対象サービスのcgroupの memory.current が memory.max に対して読み戻し分の余裕を持つことを確かめてください。
zram・zswapの圧縮方式とディスクI/Oの違い
zramはRAMの一部を圧縮したブロックデバイスにしてスワップ先に使う方式で、ディスクI/Oが発生しません。Fedoraは33以降、標準のインストールでディスクのスワップパーティションを作らずzramを使います。zswapはディスクのスワップの手前に圧縮キャッシュを置き、書き出し量を減らす方式です。どちらもCPUで圧縮する代わりにI/Oを減らすので、ディスクが遅くCPUに余裕がある小型マシンで効きます。
vm.swappinessの意味と設定方法|0〜200・既定60
値の意味:スワップとページキャッシュの回収比率
カーネルの文書は、swappinessを「スワップとファイルシステムのページングの、おおよその相対I/Oコスト」と定義しています。100で両者を同じコストとみなし、匿名ページとページキャッシュを同じ強さで回収します。100より小さいとスワップを高くつくものとみなしてページキャッシュを先に捨て、大きいとスワップを先に使います。既定値は60です。
範囲は0〜200で、Linux 5.8で上限が100から200に広がりました。100を超える値はzramやzswapのようにスワップがファイルシステムより速い構成のためのもので、文書では「スワップのランダムI/Oが平均2倍速いなら133」という計算例が示されています。「swappinessはスワップを始めるメモリ使用率のしきい値」という説明を見かけますが、しきい値ではなく、2種類のページのどちらを先に回収するかの比率です。
vm.swappinessの確認・一時変更・永続化
# 現在値
$ sysctl vm.swappiness
vm.swappiness = 60
# 一時的に変更(再起動で戻る)
$ sudo sysctl -w vm.swappiness=10
# 範囲外は拒否される(検証VMで実測)
$ sudo sysctl -w vm.swappiness=201
sysctl: setting key "vm.swappiness": Invalid argument
# 永続化
$ echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf
$ sudo sysctl --system
検証VMでは200は受け付けられ、201と-1は拒否されました。上流カーネルでは5.8未満の上限は100ですが、ディストリビューションのバックポートによって異なるため、配布元の仕様とuname -r で版を確認します。版の調べ方はLinuxのバージョン確認:OS確認コマンドの早見表とos-release・unameの使い分けにまとめています。
vm.swappiness=0と1の違い|MGLRU有効のカーネルで実測
カーネルの文書は、0のときは「空きページとファイルページがhigh watermarkを下回るまでスワップを始めない」と書いています。つまり0でも、追い詰められればスワップするという説明です。ところが、Ubuntu 24.04の検証VMで計測すると結果が違いました。
swappiness別の実測結果|0でのOOM Killと未使用スワップ
検証VM(MemTotal 2,012,932kB、スワップファイル1GiB)で、Pythonに2,300MiBのメモリを確保させ、スワップ使用量のピークを0.1秒間隔で記録しました。MGLRUは後述のカーネルの新しいページ回収方式で、Ubuntu 24.04のカーネルでは既定で有効(0x0007)です。
| MGLRU | swappiness | 結果 | スワップ使用ピーク |
|---|---|---|---|
| 有効(既定) | 60 | 確保成功 | 666,148kB |
| 有効(既定) | 1 | 確保成功 | 620,060kB |
| 有効(既定) | 0 | OOM Kill | 0kB |
| 無効 | 0 | 確保成功 | 672,084kB |
各条件の試行は次の手順で、それぞれ1回ずつ行いました。OOM Killを意図的に起こし、ホスト全体のスワップ設定とキャッシュを変更するため、再現は検証専用VMで行ってください。終了後はswappinessとMGLRUを試行前の値へ戻します。試行の前にスワップを空にし、ページキャッシュも捨てて条件をそろえています。
# 試行ごとの初期化(値は条件に合わせて変える)
$ sudo sysctl -qw vm.swappiness=0
$ sudo swapoff -a && sudo swapon /swapfile
$ sync; echo 3 | sudo tee /proc/sys/vm/drop_caches
# スワップ使用量のピークを0.1秒間隔で記録する(別の端末で実行)
$ peak=0; while true; do u=$(awk '/SwapTotal/{t=$2}/SwapFree/{f=$2}END{print t-f}' /proc/meminfo); [ $u -gt $peak ] && peak=$u && echo $peak; sleep 0.1; done
# 2,300MiBを確保して書き込む(cgroupのメモリ上限なし)
$ sudo systemd-run --wait --collect python3 -c "x=b'a'*(2300<<20)"
# cgroupのメモリ上限ありの試行(300MiBを確保)
$ sudo systemd-run --wait --collect -p MemoryMax=128M -p MemorySwapMax=512M python3 -c "x=b'a'*(300<<20)"
MGLRUが有効だと、0のときだけスワップが一度も使われず、1GiBの空きを残したままOOM Killされました。2,000MiBに減らしても同じです。MGLRUを止める(echo n | sudo tee /sys/kernel/mm/lru_gen/enabled)と、0でも約656MiBをスワップして確保に成功し、文書どおりの挙動に戻りました。
メモリ上限を持つcgroupの中でも差が出ます。systemd-runでMemoryMax=128M・MemorySwapMax=512Mを付けたサービスに300MiBを確保させると、60と1は成功し(VmRSS約133MiB・VmSwap約177MiB)、0だけがOOM Killされました(検証ログに記録された試行の経過時間は139ミリ秒)。
0でスワップしない理由:MGLRUとcgroup回収の分岐
理由はカーネルのソース mm/vmscan.c にあります。MGLRUは回収するページの種類の下限を min_type(swappiness) で決めており、swappinessが0だと匿名ページが回収対象から外れ、ファイルページしか回収しません。MGLRUはLinux 6.1で取り込まれ、文書にあるとおり CONFIG_LRU_GEN_ENABLED=y でビルドしたカーネルでは既定で有効になります。
従来のLRUでも、cgroupの上限による回収では0でスワップしません。同じファイルの get_scan_count() には「全体の回収ではswappinessが0でもOOMを防ぐためにスワップするが、memcg(cgroup)では0でスワップを完全に止めたい利用者がいる」という趣旨のコメントがあり、cgroup回収かつ0ならファイルページだけを回収します。cgroup v2ではcgroupごとのswappinessが無く、グローバルの vm.swappiness がそのまま使われるため、ホストで0にするとDockerのコンテナやsystemdのサービスも上限到達時にスワップしなくなります。
# MGLRUが有効か(最下位ビット0x0001が立っていれば有効)
$ cat /sys/kernel/mm/lru_gen/enabled
0x0007
vm.swappinessの用途別推奨値とswapoffとの使い分け
- 一般的なサーバー:既定の60で始め、応答時間やメモリ・I/OのPSI、cgroup上限を確認する。スワップI/Oが性能低下の原因なら、10前後への変更を試し、変更前後の性能を比較する。
- データベース:MariaDBの公式ドキュメントは1を推奨。MongoDBの本番運用ノートは1または0としているが、MGLRUが有効なカーネルでは0がスワップ無効と同じになるため1を選ぶ。
- zram・zswap:スワップがディスクより速いので、100超も検討対象になる。
- スワップを使わせたくない:0ではなく
swapoffで明示的に止め、fstabからも外す。挙動が文書と食い違わない。
MySQLのリファレンスマニュアルでは、swappinessの具体値の記載を確認できませんでした。MySQLで1を試す場合も、スワップを相対的に抑える調整として扱います。バッファプールが退避されないことを保証する設定ではありません。MGLRUが有効なカーネルでは、0と1の差は「スワップの量」ではなく「スワップできるかどうか」です。ここを取り違えると、空きスワップがあるのにOOM Killされる設定になります。
クラウド・コンテナでのスワップ運用|EC2・Docker・Kubernetes
AWS EC2でスワップを追加するときの判断
EC2でも、まず swapon --show でスワップの有無を確かめます。何も表示されなければスワップはありません。スワップを作らないOSイメージもあり、今回の検証に使ったUbuntu 24.04のVMも起動直後はSwapが0Bでした。追加する手順は前述のスワップファイルと同じで、サイズは前章のAWSの表(2GiB超・64GiB未満ならRAMの0.5倍)が目安です。ただし、小さいインスタンスでOOMが出たときのスワップは応急処置です。本番でスワップに頼り続けるなら、インスタンスタイプの引き上げやメモリ使用量の見直しを先に検討してください。インスタンス選定を含めた構成の見直しは、インフラ構築(AWS・Google Cloud・Azure)で支援しています。
Dockerコンテナ・systemdサービスのスワップ上限設定
cgroup v2では、コンテナやサービスごとのスワップ上限は memory.swap.max で決まります。Dockerの --memory-swap は「メモリとスワップの合計」なので、差がスワップの上限です。検証VMでの値は次のとおりでした。
$ docker run --rm --memory 128m busybox cat /sys/fs/cgroup/memory.swap.max
134217728
$ docker run --rm --memory 128m --memory-swap 512m busybox cat /sys/fs/cgroup/memory.swap.max
402653184
--memory だけを付けると、メモリと同じ128MiBまでスワップできます。512mを指定すると、差の384MiBが上限になりました。同じコンテナで300MBのバッファを使う dd は、swappiness 60では成功し、0では Killed になりました。systemdのサービスなら MemorySwapMax= で同じ上限を付けられます。systemdの基本はsystemdとは?読み方・SysVinitとの違いとsystemctl・ユニットファイルの基本【v262】を参照してください。この記事の検証VMはColimaとは?MacでDocker Desktopを外す導入手順と設定の落とし穴【0.10系】で紹介しているColimaで、検証専用のプロファイルとして立てました。
Kubernetesのノードswapは1.34で安定版
Kubernetesはスワップが有効なノードではkubeletを起動しないのが既定で、長くスワップ無効が前提でした。公式ドキュメントによると、NodeSwap機能は1.22でアルファ、1.28でベータ、1.30でベータのまま既定有効になり、1.34で安定版になりました。2026年10月時点の最新は1.37系です。
安定版になっても、使うにはkubeletで failSwapOn: false と memorySwap.swapBehavior: LimitedSwap を設定する必要があります。既定のNoSwapではPodはスワップを使いません。LimitedSwapでもスワップを使えるのはBurstable QoSのPodだけで、GuaranteedとBestEffortのPodや優先度の高いPodは対象外です。公式は暗号化したスワップと、システムのI/Oと競合しない専用ディスクを推奨しています。ノードの vm.swappiness を0にすると、前章のとおりLimitedSwapのPodも上限でスワップしなくなる点にも注意してください。ノード上でメモリを奪い合う問題の切り分けはノイジーネイバー(Noisy Neighbor)問題とは?原因の切り分けと対策をKubernetes・SaaS別に解説で扱っています。
スワップが多発したときの切り分け手順
- 本当に害が出ているか:
vmstat 1でsi・soが何秒も続けて非ゼロか。使用量が多くてもsi・soが0なら、その観測期間にはスワップI/Oはほぼありません。ただし、メモリ不足やページキャッシュの回収による遅延は否定できないため、応答時間とメモリの余裕も確認します。 - 誰が退避されているか:VmSwapのワンライナーでプロセスを特定する。
- 原因の分類:特定プロセスのRSSが時間とともに増え続けるならメモリリークを疑う。全体として足りないならRAMの増設かインスタンスの引き上げ。空きRAMがあってもcgroupの上限でスワップするため、上限と性能への影響を確認してからswappinessの変更を試す。
原因を見ないままswappinessを0にしたりスワップを削除したりすると、空きRAMが少ない環境では安全弁を外すことになります。
よくある質問
Linuxにスワップは必要ですか?
無効にもできますが、通常は少量でも置くことを勧めます。スワップが無いと、通常の匿名ページを退避できず、ページキャッシュや回収可能なカーネル内キャッシュの解放で空きを確保することになります。スワップを無効にする場合は、必要なRAMとOOM時の運用を先に設計します。KubernetesのNoSwapはPodのスワップ利用を禁止する設定であり、ノード上のスワップ自体を無効にする設定ではありません。
スワップをクリアするにはどうすればいいですか?
再有効化する全領域がfstabに登録され、noautoが付いていない場合は、sudo swapoff -a && sudo swapon -a で、退避中のページをRAMへ戻してから再有効化します。ホストのメモリ不足やcgroupのメモリ上限への到達によって、swapoffは Cannot allocate memory で失敗し、プロセスがOOM Killされることもあるので、先に free -h のavailableと、対象サービスのcgroupの上限に余裕があるかを確認してください。
vm.swappinessの0と1は何が違いますか?
MGLRUが有効なカーネル(Ubuntu 24.04など)では、0にすると匿名ページを一切スワップしません。1は匿名ページの回収を許すため、メモリ圧迫時にスワップを利用できます。ただし退避量が少量に制限されるわけではありません。検証では、0だけが1GiBの空きスワップを残してOOM Killされました。スワップを最小限にしたい場合も、0ではなく1にしてください。
kswapd0とは何ですか?
カーネルのページ回収スレッドkswapdのうち、NUMAノード0を担当するものです。空きページが少なくなると起こされ、ページキャッシュの解放や匿名ページのスワップアウトを行います。kswapd0のCPU使用率が高止まりするなら、メモリが足りず回収が続いている状態です。
スワップの使用率が100%でも問題ありませんか?
スワップが満杯だと、それ以上匿名ページを退避できません。si・soが0でも安全とは限らないので、RAMの空き、回収可能なキャッシュ、cgroupのメモリ上限を確認します。si・soも続いている場合は、必要なメモリを回収できなければOOM Killが起きる可能性があるため、負荷の削減やRAMの追加を検討します。