Red Hat Enterprise Linux 8系とRed Hat Enterprise Linux 9系でメモリの使い方やOSスワップの挙動が異なる現象を紹介します。
8系と 9系と書いたのは、クローンOSのRocky Linux 8とRocky Linux 9、AlmaLinux 8とAlmaLinux 9なども同じ動作になるからです。
(以降、Red Hat Enterprise LinuxはRHELと記載)
RHEL8が、空きメモリがあるのにスワップする動きをして教科書通りの動作をしない場合があります。
メモリとファイルへのアクセスパターンなどにも依存しているので、他にもいろいろパターンはありそうですが、今回は一例の紹介です。
検証環境 RHEL8 , RHEL9
クローンOSのRocky Linuxを利用しています。
・Rocky Linux 8.10 (以降はRHEL8と表記)
・Rocky Linux 9.4 (以降はRHEL9と表記)
物理メモリ64GB、スワップサイズ20GB
OSのメモリ管理
OSは以下の挙動をします。
(1)物理メモリを使用
(2)物理メモリが足りなくなったら、スワップ領域(ストレージ)を使用
(3)更にメモリが足りなくなったら、OS自体が稼働不可になるのことを防ぐために、OOMKiller(Out of Memory Killer)機構によりメモリを多く使用しているプロセスを強制停止し空きメモリを作ります

検証パターン(メモリ確保+ファイル操作)
今回の検証では、C言語のプログラムでメモリ確保とファイルへのアクセスを行います。
Javaでも同じ現象が発生します。プログラミング言語の依存はありません。
(1)20GBのメモリを確保(malloc)して、メモリへデータを書き込む。
(2)20GBのファイルを読み込む(各プロセスが同じファイルをread)
30秒間隔で3回プログラムを実行しています。
物理メモリ64GBの環境で、20GBx3回=60GBのメモリを使用し、空きメモリが少ない状態を作り出す。
実行手順はこんな感じです。
#(1) 20GBのファイル作成。
dd if=/dev/urandom of=largefile.dat bs=1M count=20480
#(2) ディスクキャッシュをクリア
sudo sysctl -w vm.drop_caches=3
#(3) 20GB メモリ確保, 20GBの同一ファイルをread
# 3つ起動。
./testpro &
sleep 30
./ testpro &
sleep 30
./ testpro &
sleep 30
データの収集と分析方法を以下の記事を参照ください。

OSの各値を検証
freeコマンドとdstatコマンドのデータで分析します。
freeコマンドの値の意味
free -h コマンドの出力値の読み方
各項目の意味
Mem:
・total: システム全体のメモリの総量
・used: 現在使用中のメモリの量
・free : 現在使用されていないメモリの量
・shared: 複数のプロセスで共有されているメモリの量
・buff/cache: バッファとキャッシュとして使用されているメモリの量
・available: 即座に使用可能なメモリの量
Swap:
スワップパーティションの状況を示します。total, used, free はそれぞれ、スワップパーティションの総量、使用量、空き容量を表す。
<スワップ発生時の例>

OSのメモリの利用状況を確認
freeコマンドでメモリの利用状況を確認します。
左がRHEL8、右がRHEL9。10秒ごとにfreeコマンドで採取。

見やすいようにavailableとsharedをグラフから削除
RHEL8とRHEL9で、空きメモリが少なくなってからのbuff/cache(キャッシュ)とused(物理メモリ)の使用量の動きに差があります。

RHEL8
空きメモリが少ない状態でファイルキャッシュとして使用し、スワップも利用する。
ファイルキャッシュを優先する動作をする。
ファイルキャッシュを解放して、メモリに割り当てて、スワップさせないほうが性能的には優位だと思います。
違和感がある挙動ですね。
ディスクI/Oとスワップアウト・インを確認
ディスクのI/OとPage IN/Page OUT(スワップインとスワップアウト)を確認します。

RHEL8
スワップアウトにより、ディスクへの書き込みが発生して、OSのパフォーマンスを低下させている可能性がある。
RHEL9
メモリをファイルキャッシュとして優先的には使用せずにスワップもしない。
教科書的なシンプルな動作。
RHEL8の挙動に問題あるから、RHEL9で改善したのかもしれませんね。
ちなみにswappinessなどの設定変更しても効果はありませんでした。
RHEL8で物理メモリがなくなるまで、極力スワップしない設定(sysctl vm.swappiness=0)でも効果なし。
RHEL8のスワップ回避方法
回避方法を発見したので、追記します。
Red Hatの公式に以下の情報があります。
この記事に書いた現象そのもののタイトルです。。。。
[再利用するページキャッシュが十分あるにもかかわらず早すぎるタイミングでスワッピングが発生する]
https://access.redhat.com/ja/solutions/6997775
全体を見るのは、ログイン・アカウントが必要です。
vm.force_cgroup_v2_swappiness=1 と vm.swappiness=10 などを設定すれば回避できます。
vm.swappinessの値はマシン環境によって微調整が必要。
便利なサービスの紹介
自分でOSの種類と物理メモリのサイズを準備するのは大変ですよね。
今回のような検証に適したサービスがあります。
Hyper-VやVirtual Boxなどのローカルの仮想環境でメモリ16GB以上の環境を準備するのは手元にメモリが潤沢なマシンが必要。
このサービスを使うと、サーバの込み具合に依存すると思いますが、32GBメモリのVMが1クリックで5分程度で利用可能になります。

OSの種類と物理メモリのサイズで選択可能です。こういった実験には便利ですね。
以下は、私も利用しているConoHa VPSの管理画面です。
時間課金が使えるので最適ですね。32GBメモリで1時間53.3円です。