このトピックでは、Linux の Elastic Compute Service (ECS) インスタンスで slab_unreclaimable メモリの使用量が高い問題を診断し、解決する方法について説明します。
問題の説明
Linux インスタンスで cat /proc/meminfo | grep "SUnreclaim" コマンドを実行して SUnreclaim の値を確認すると、SUnreclaim の値が大きい (例:SUnreclaim: 6069340 kB) ことがわかります。これは、slab_unreclaimable メモリが大量に存在することを示しています。slab_unreclaimable メモリが総メモリの 10% を超える場合、スラブメモリがリークしている可能性があります。
原因
Linux カーネルは、スラブアロケータというメカニズムを使用してメモリを管理します。このシステムは、専用のキャッシュから同じサイズのメモリオブジェクトを割り当てたり解放したりすることで、メモリの断片化を減らすのに役立ちます。slab_unreclaimable メモリとは、メモリが圧迫されている状況下でもカーネルが回収できないスラブキャッシュの部分を指します。このメモリは、dentry (ディレクトリ エントリ) や inode キャッシュなどのアクティブなオブジェクトを保持しているため、回収できません。これらのキャッシュはファイルシステムのパフォーマンスを向上させますが、過度に増大して容易に解放されないことがあり、slab_unreclaimable メモリの高い使用量を引き起こし、時には OOM (Out-of-Memory) Killer をトリガーすることもあります。
ソリューション
Linux インスタンスに接続してトラブルシューティングを行います。
詳細については、「ECS インスタンスへの接続方法」をご参照ください。
メモリが解放できず、
オブジェクトの数が最大であるか、またはメモリ量が最大であるスラブの名前を確認します:objectsの数が最大であるか、またはメモリ量が最大であるスラブの情報を表示します。slabtop -s -aコマンドの出力で、
OBJ/SLAB列の値が最も大きいスラブの名前 (NAME列の値) を確認し、記録します。スラブメモリが回収不能かどうかを確認します。
次のコマンドで、
<slab NAME>を、前の手順で取得したOBJ/SLAB列の値が最も大きいスラブの名前に置き換えます。cat /sys/kernel/slab/<slab NAME>/reclaim_accountたとえば、次のコマンドを実行して、
kmalloc-192という名前のスラブのメモリが回収可能としてマークされているかどうかを判断できます。cat /sys/kernel/slab/kmalloc-192/reclaim_accountスラブメモリが回収不能な場合は、コマンドの出力に 0 が表示されます。スラブメモリが回収可能な場合は、コマンドの出力に 1 が表示されます。
slab_unreclaimable メモリの使用率が高い原因を特定します。
crash ツールを使用して静的解析を行うか、perf ツールを使用して動的解析を行うことで、スラブメモリリークの原因を特定できます。このトピックのシナリオ例では、
kmalloc-192という名前のスラブでメモリリークが発生しています。方法 1:crash を使用した静的解析
次のコマンドを実行して crash ツールをインストールします。
sudo yum install crash -y次のコマンドを実行して kernel-debuginfo ツールをインストールします。
Alibaba Cloud Linux 3
sudo yum install -y kernel-debuginfo-<kernel version> --enablerepo=alinux3-plus-debug説明kernel versionをシステムの実際のカーネルバージョンに置き換えてください。uname -rコマンドを実行してカーネルバージョンを照会できます。Alibaba Cloud Linux 2
sudo yum install kernel-debuginfo -y
次のコマンドを実行して crash ツールを起動します。
sudo crashcrash で次のコマンドを実行して、
kmalloc-192のメモリ統計情報を表示します。kmem -S kmalloc-192大量のメモリ統計データがある場合は、最後の数行のみを表示するように指定できます。たとえば、次のコマンドを実行して、データの最後の 10 行を表示できます。
kmem -S kmalloc-192 | tail -n 10コマンド出力の例:
SLAB MEMORY NODE TOTAL ALLOCATED FREE ffffea004c94e780 ffff88132539e000 0 42 29 13 ffffea004cbef900 ffff88132fbe4000 0 42 40 2 ffffea000a0e6280 ffff88028398a000 0 42 40 2 ffffea004bfa8000 ffff8812fea00000 0 42 41 1 ffffea006842b380 ffff881a10ace000 0 42 41 1 ffffea0009e7dc80 ffff880279f72000 0 42 34 8 ffffea004e67ae80 ffff881399eba000 0 42 40 2 ffffea00b18d6f80 ffff882c635be000 0 42 42 0コマンドの出力は、
ffff88028398a000の空きメモリ量 (FREE列の値) が少なく、割り当て済みメモリ量 (ALLOCATED列の値) が多いことを示しています。crash で次のコマンドを実行して、
ffff88028398a000のメモリデータを表示します。rd ffff88028398a000 512 -Sコマンドの出力に大量のデータが含まれている場合は、出力をページ単位で表示させることができます。
たとえば、コマンドの出力で
put_cred_rcu関数が複数回繰り返される場合は、Linux カーネルのソースコードを確認し、put_cred_rcu関数を検索できます。void __put_cred(struct cred *cred) { call_rcu(&cred->rcu, put_cred_rcu); }put_cred_rcu関数は、`cred` 構造体を非同期に解放するために使用されます。put_cred_rcuが `cred` 構造体の末尾に存在することは、カーネル内でスラブメモリリークが発生していることを示します。
方法 2:perf を使用した動的解析
次のコマンドを実行して perf ツールをインストールします。
sudo yum install perf -y次のコマンドを実行して、perf を使用して 200 秒間隔で
kmalloc-192内で解放されていないメモリを動的に取得します。sudo perf record -a -e kmem:kmalloc --filter 'bytes_alloc == 192' -e kmem:kfree --filter ' ptr != 0' sleep 200動的に取得したデータをカレントディレクトリの一時ファイルに保存します。
この例では、一時ファイル名は testperf.txt です。
sudo perf script > testperf.txt次のコマンドを実行して、testperf.txt の内容を表示します。
cat testperf.txt空きメモリ (
free) がないスラブメモリを手動で特定し、Linux カーネルのソースコードでスラブメモリリークを引き起こしている関数を手動で照会する必要があります。
crash や perf などのツールを使用して、メモリリークに関連する関数呼び出しパスや影響を受けるカーネルデータ構造を特定した後、カーネル開発者や専門の O&M (運用保守) 担当者の指導の下でメモリリークの具体的な原因を特定し、メモリリークの問題を解決することを推奨します。
次のソリューションを検討してください。
カーネルのアップグレードまたはパッチの適用。
カーネルパラメーターの調整。
影響を受けるサービスまたはモジュールの再起動。
アプリケーションまたはドライバーの最適化。
システムの再起動。
関連ドキュメント
スラブメモリリークは、インスタンス上のアプリケーションで利用可能なメモリを減少させ、メモリの断片化を引き起こします。これにより、システムの OOM Killer がトリガーされ、システムパフォーマンスの変動を引き起こす可能性があります。