WSL2によるホストのメモリ枯渇を防ぐための暫定対処

5/27にWindows 10 2004 Updateが正式リリースされて、それに伴いWSL2もWindows Insider Preview版を使用しなくても利用可能となりました:tada:

しかしWSL2を使用してるとVmmemというプロセスのメモリ使用量が増加し続けて、Windowsホストのメモリが枯渇してしまう問題があります。そしてこの問題は正式リリース後も解消されず残っています。

> NOTE: Windowsホストで動いているVmmemプロセスで「WSL2のHypver-V仮想マシン全体が消費＆確保しているCPUとメモリ」を確認できるという大雑把な理解でOKです。

microsoft/WSLのIssueは１年近くOpenのままとなっており、Microsoft側も問題を認識して取り組んでいるものの未だに根本対処されていません。

https://github.com/microsoft/WSL/issues/4166

# 本問題のアップデート情報 (2023/07/04更新)

本問題に関する重要なアップデート情報はここに追記する予定です。
なお本問題が根本的に解決されたタイミングで更新時に変更通知を送る予定です。

- 2023/07/04 - Issueは完全に放置されて全く進捗ないように見えるのですが、microsoft/WSLのレポジトリの[1.3.10](https://github.com/microsoft/WSL/releases/tag/1.3.10)リリース(Pre-release扱い)の中で`Memory reclamation idle detection, drop_caches. To use add the below to %userprofile%\.wslconfig`という試験的な機能がアップデートが含まれているようです。私の開発環境は既にmacOS, Linuxに置き換わってるため実際に試して確認することはできませんが、興味がある方はこのリリースに対応したWSLを導入して試す価値はあるかもしれません。(そして可能であればその試した結果をこちらの記事のコメントか、新しい記事などにしてリンクなどを共有していただけると助かります)
    ```
    [experimental]
    autoMemoryReclaim=true
    ```
- 2021/10/16 - IssueはロックされたままWSL開発者のコメント等も一切なく対応されていない状態です。このため2021年10月時点でも引き続きこの記事の暫定対処が必要です。
    - ただしコメントでも指摘があったとおり、現在のWSL2ではデフォルトのメモリサイズが`PC搭載メモリの50%または8GBのうち、少ない方の値`に変更されたため、ホストメモリ枯渇の問題はある程度改善されています。([Build 20175リリースノート](https://docs.microsoft.com/ja-jp/windows/wsl/release-notes#build-20175))
    - WSL2はWSLgやLinuxカーネルも含めた大型のアップデート、WSL2のOSからの分離(ストアアプリとして提供)等の大きな動きが続いているため、少なくともこれらの作業が落ち着くまでは根本対処は盛り込まれないと推測しています。
- 2020/07/15 - 特に表立った動きなし。
    - 6/25頃に開発者が対処を盛り込んだPRがある([comment](https://github.com/microsoft/WSL/issues/4166#issuecomment-649230583))と発言しているため対応は進んでいる可能性が高い。
    - 7/7頃にIssueが荒らされたことにより、Issueがロックされた状態となっている。
- 2020/06/03 - WSL開発メンバが本問題の原因がLinuxファイルキャッシュによるものであること([Link](https://twitter.com/richturn_ms/status/1267528045251854336?s=20))、暫定対処がメモリサイズの固定化であること([Link](https://twitter.com/craigaloewen/status/1267862768356429824?s=20))、本問題を現在対処中であること([Link](https://twitter.com/craigaloewen/status/1267867503394295809?s=20))をTwitterに投稿しています。


# 結論: 暫定対処でメモリサイズを固定する

WSL2のメモリサイズを固定することでWindowsホストのメモリ枯渇を防ぎます。
原因について興味がある人は後述しているのでそちらを参照してください。

`%USERPROFILE%\.wslconfig`のコンフィグに以下の設定を行います。

- `%USERPROFILE%`は`C:\Users\taro`のようなホームディレクトリを指します。
- コンフィグが存在しない場合は作成してください。
- 設定した後はWSL2を再起動するか、OSを再起動してください。

```properties
[wsl2]
memory=6GB
swap=0
```

- `memory`にはWSL2が最大確保するメモリサイズを指定します。PCの搭載メモリとWSL2の使用用途に応じてメモリサイズの値は変更してください。ちなみに**未指定時のデフォルト値は`PC搭載メモリの50%または8GBのうち、少ない方の値`**です。(WSL2バージョンがBuild 20175未満の場合は`PC搭載メモリの80%`)
- (個人的に推奨) `swap`を0に設定してスワップを無効にします。
    - スワップを無効にするとWSL2側で実際にメモリ不足になった途端に変な挙動を起こすかもしれません。しかしその代わりSSD等で大量のスワップIn/Outの発生を防ぐため、大量書き込みによるSSDの寿命を縮めることは回避できます。トレードオフを意識して設定するかどうかを考えましょう。

メモリサイズの上限を設定しなくてもいい感じに使えるのがWSL2の利点の一つだと考えているので、この暫定対処は心苦しいですが致し方ないところです。早く何らかの形で改善してほしいものです。

## 何が原因か？

Issueの中では、以下のLinuxとWSL2の特性が相まって問題を引き起こしてることが指摘されてます。

- Linuxでは積極的にファイルアクセス時にファイルキャッシュをメモリ上に配置する(freeコマンドにおけるbuff/cache使用量の増加)。ファイルを削除したり、プロセス等がメモリを確保できなくなるくらいにメモリ不足になるまでは基本的にファイルキャッシュは確保したまま。
- WSL2は仮想マシン上のLinuxのメモリ使用量に応じて、仮想マシンのメモリサイズを動的に増減させる。
    - 当然ですが仮想マシンのメモリサイズはLinuxのメモリ使用量よりも多く確保します。

それぞれの特性により、開発等の時の大量のファイル、ライブラリ、DockerイメージなどにアクセスするとLinuxのファイルキャッシュを中心に大量のメモリを確保し、それに合わせてWSL2が仮想マシンのメモリサイズを増加させていきます。WSL2が仮想マシンのメモリサイズを増加させるため、Linuxでは「まだメモリに余裕がある」と判断してファイルキャッシュをさらにため込んでという悪循環を引き起こします。

これによってメモリ16GB搭載のPCはもちろんメモリ32GB搭載のPCでも比較的簡単にWindowsホストのメモリが枯渇することになります。

以下はWSL2のメモリサイズを6GB固定にした環境でのVmmemおよびLinuxのメモリ使用量です。Linuxのメモリ使用量はfreeコマンドの結果で確認しています。大まかな目安として参考にしてください。

```bash
# - Vmmemメモリ使用量=3.5GBの時のWSL2のLinxuメモリ使用量
# - Linuxメモリ使用量=3.1GB(内、buff/cache使用量が2.6GB)
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           5.8G        479M        2.7G        1.2M        2.6G        5.1G

# - Vmmemメモリ使用量=1.7GB
# - Linuxメモリ使用量=0.6GB(内、buff/cache使用量が183MB)
$ free -h
              total        used        free      shared  buff/cache                 total        used        free      shared  buff/cache   available
Mem:           5.8G        457M        5.2G        1.2M        183M        5.2G
```

## 本問題が起きやすいユースケース

以下のユースケースではホストのメモリ枯渇が発生しやすいです。特にこれらのユースケースが組み合わさると本問題はより発生しやすくなります。

- Visual Studio Code + Remote Extension(WSL)を使用してる
- Docker Desktop WSL2を使用してる
- 大きなコードベースで開発してる

上記の組み合わせが「Windows上でのUnix/Linux環境開発」の主流の一つになる可能性が高いですが、その組み合わせが本問題を引き起こしやすいというのは皮肉なものです。

## その他の暫定対処方法

WSL2のメモリサイズを固定しない暫定対処について紹介します。
いずれも手動で実行する必要があり、自動運用するためには何らかの工夫が必要となるため積極的にはお勧めしません。

### キャッシュをドロップ

基本的にメモリサイズ固定が最も効果的ですが、メモリサイズを可変のままにしたい場合は定期的にLinuxのキャッシュをドロップすることである程度は症状を抑えることもできます。

```
sudo sh -c "echo 3 >'/proc/sys/vm/drop_caches' && swapoff -a && swapon -a"
```

ただし、WSL2ではsystemdやcronなどが稼働していないため、そのあたりの準備が必要なことを考えるとメモリサイズ固定が一番手軽です。

### WSL2を再起動

以下のURLの情報を参考にWSL2を一度再起動すれば、WSL2の仮想マシンがファイルキャッシュ等がクリアされた状態で立ち上がります。

[WSL を再起動する方法](https://kagasu.hatenablog.com/entry/2020/01/02/155532)

ちなみにWSLの再起動などの操作についてはWSL1もWSL2も基本的に同じです。

# 参考URL

WSL2やLinuxのメモリ管理などについて少し踏み込みたい人向けの参考URLが中心です。
(コメント欄でいただいたものも追記してます)


- [本問題のIssue](https://github.com/microsoft/WSL/issues/4166)
    - Issueがロックされたため開発者以外がコメントできない状態のまま止まっています。
- [Configure global options with .wslconfig](https://docs.microsoft.com/en-us/windows/wsl/wsl-config#configure-global-options-with-wslconfig) 
    - 公式のWSL2コンフィグ設定リファレンスです。
- [Windows Subsystem for Linux 2のメモリ管理を詳しく見る](https://ascii.jp/elem/000/001/981/1981180/)
    - Linuxのファイルキャッシュ周りには触れてませんが、WSL2とVmmemのメモリ管理について検証を含めた情報が載ってます。
- [いまさら聞けないLinuxとメモリの基礎＆vmstatの詳しい使い方](https://qiita.com/kunihirotanaka/items/70d43d48757aea79de2d)
    - ファイルキャッシュ等を含むLinuxのメモリ周りの基礎。
- [減り続けるメモリ残量！ 果たしてその原因は!?](https://www.atmarkit.co.jp/ait/articles/0810/01/news134.html)
    - こちらもファイルキャッシュ等を含むLinuxのメモリ周りの基礎。図がやや豊富で見やすいため、よりイメージしやすいかもしれません。