WSL2使用時のメモリ枯渇への対処

# WSL2使用時のメモリ枯渇問題

WSL2を使用しているとVmmemというプロセスのメモリ使用量が増加し続け(Linuxがファイルキャッシュ中心にメモリを確保し続け、開放もしてくれない)、Windowsホストのメモリが枯渇してフリーズしてしまいます。

以下の条件で発生しやすく、これらが組み合わさるとより発生しやすくなるようです。筆者の環境でもこの問題が発生しましたが、1と2が当てはまっていました。

1. VSCode + Remote Extension(WSL)の使用
2. Docker Desktop WSL2の使用
3. 大きなコードベースでの開発

この問題については以下の記事で詳細に解説してくれているので参考にしてください。

https://qiita.com/yoichiwo7/items/e3e13b6fe2f32c4c6120

# 対処方法
対処は大きく2つ、
- WSL2のメモリサイズの上限を設定
- 定期的なキャッシュクリア

です。以下に方法を記載しておきます。

## 対処方法1: WSL2のメモリサイズの上限を設定
C:\Users\ユーザー名\\.wslconfigに設定を行います。.wslconfigが存在しない場合はご自身で作成してください。

```.wslconfig
[wsl2]
memory=8GB
swap=0
```
- `memory`でWSL2が最大確保するメモリサイズを指定します。メモリサイズはご自身の環境に応じて変更してください。未指定時のデフォルト値はPC搭載メモリの50%または8GBのうち、少ない方の値になります。
- `swap=0`にすることでメモリ枯渇時の記憶媒体への負担を回避します。


.wslconfigに上記の内容が記載できたら、WSL2の再起動(PowerShellでwsl --shutdown)かWindowsの再起動をしてくだい

:::note warn
swapを設定(`swap=4GB`など)するとメモリ枯渇時に割り当てた分だけディスクを仮想メモリとして扱うことができ、擬似的にメモリサイズの上限を追加できます。ただし、ディスク容量が少ないPCなどでは厳しいですし、SSDなどの記憶媒体に負担がかかり寿命に影響します。


個人的には`swap=0`にしていますが、swapを設定することで後述するWSL2のフリーズを防げている例もありますので、ご自身の判断によって設定してみても良いかもしれません。
- [WSL2のメモリ設定でswapを活用](https://fungineer.net/wsl2-memory-setting/)
:::

### 解決?
上記の設定で解決!!と思いきや、メモリが上限に達するとWindowsのフリーズは起こりませんがWSL2がフリーズしてしまい、Dockerやリモート接続したVSCodeが使えず、WSL2を再起動することになります。


メモリ割り当ての上限値によってはこれが頻繁に発生するのでかなりストレスです。ですので次のキャッシュクリアについても確認しておくと良いと思います。

## 対処方法2: 定期的なキャッシュクリア
キャッシュクリアについては3つ方法があります。
1. 試験機能の`autoMemoryReclaim`
2. 手動でキャッシュクリアコマンドの入力
3. キャッシュクリアコマンドの自動化

### 1.試験機能の`autoMemoryReclaim`

:::note warn
この機能については、2023/12現在、Windows11またはWindows10の一部でしか使えないようです。筆者の環境では使えなかったので参考になりそうな記事の紹介に留めます。
:::

https://zenn.dev/takajun/articles/3acd9f77c73bb4

### 2.手動でキャッシュクリアコマンドの入力

以下のコマンドをWSL2のLinuxディストリビューション上(ここでは以降Ubuntuとします)で実行してください。
```bash
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches'"  # swap=0の場合
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches' && swapoff -a && swapon -a" # swap設定値ありの場合

```

### 3.キャッシュクリアコマンドの自動化
コマンドをいちいち実行するのは面倒なので自動化します。自動化にはcronを使います。
#### cronの自動起動

WSL2でcronはデフォルトだと停止しているので、自動的に起動するようにします。

① まずsystemdを有効化します。Ubuntuでetc配下にwsl.confを作成します。
```
sudo vi /etc/wsl.conf
```

② wsl.confに以下の行を追加してください。
```wsl.conf
[boot]
systemd=true
```
    
③ 保存したらPowerShellを開き、WSL2を再起動します。
```
wsl --shutdown
```
    
④ 再起動できたらUbuntuを開きcronが自動的に起動しているか確認します。
```
systemctl status cron
```
`Active`の欄が`active (running)`になっていればOKです。なっていない場合は`sudo systemctl enable cron`で自動起動を有効にしてください。

#### コマンドの自動化

cronの自動起動ができたらコマンドの自動化を行います。

① まずUbuntuで任意の場所にシェルスクリプトを作成します。今回は/home/ユーザー名 の配下にdrop_cache.shを作成します。任意のユーザーに切り替えをお忘れなく。
```drop_cache.sh
#!/usr/bin/bash
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches'"; # swap=0の場合
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches' && swapoff -a && swapon -a"; # swap設定値ありの場合
```
② drop_cache.shに実行権限を付与します。
```
chmod 744 drop_cache.sh
```
③ 次にsudoのパスワード入力を省略する設定をします。今回は特定ユーザーのshコマンド実行時だけsudoのパスワード入力を省略します。`which sh`でshのパスを取得しておいてください
```
which sh
sudo visudo
```
```/etc/sudoers.tmp
# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL
```
の下に次のような形式で入力します。
```
ユーザー名 ALL=NOPASSWD: shのパス 
```

④ 最後にcronに設定を行います。

```
crontab -e
```

```
0 */2 * * * /home/ユーザー名/drop_cache.sh
```
ここでは2時間ごとに実行するようにしていますが、以下のリンクなどを参考に、ご自身の環境に合わせてカスタマイズしてください。

https://qiita.com/irico/items/a9c0bf3624a3e38146dc

⑤ (任意) ここまででキャッシュクリアの自動化はできていると思いますが、実行結果を確認したい場合はdrop_cache.shに`date`や`free -h`などを追記した上で適当なファイルに標準出力・エラー出力を記録します。

```drop_cache.sh
#!/usr/bin/bash
date
free -h
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches'"; # swap=0の場合
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches' && swapoff -a && swapon -a"; # swap設定値ありの場合
free -h
```
```
crontab -e
```
```
0 */2 * * * /home/ユーザー名/drop_cache.sh >> /home/ユーザー名/testlog.txt 2>&1
```

(もしエラーが出て実行できていなかった場合はエラー文を参考に設定し直してください。)

# 終わりに
ご指摘などあればコメント欄に書いていただければ幸いです。