# メモリ修正効果の計測結果（PLAN_02 優先度2） (Memory Fix Effect Measurement)

: 2026-06-23
対象: PLAN_02 優先度2「`ColorCorrector.ConvertImage` の中間 Mat 未 Dispose（OOM リーク）」修正
計測者: 自動ベンチ（NUnit、ヘッドレス実行・カメラ実機不要）

## 目的 (Purpose)

修正前（中間 Mat を解放しない＝旧実装）と修正後（入れ子 `using` で解放）で、連続処理時のメモリ使用量の「増え方」がどれだけ変わるかを同一条件で定量比較する。

## 方法 (Method)

- ベンチ: `TIASshot.Tests/ConvertImageMemoryBenchmark.cs`（`[Explicit]`）。
  - 修正後 = 実コード `ColorCorrector.ConvertImage`（using 版）。
  - 修正前 = 旧実装を忠実に再現（`flatten`/`extended`/`converted`/`convertedImage` を解放しない）。
  - 同一画像を N 回反復し、各反復後に `Process.PrivateMemorySize64` / `WorkingSet64` をサンプリング。
- 指標: Mat はネイティブメモリのため `GC.GetTotalMemory` では捕捉できない → `PrivateMemorySize64`（コミット量）を主指標とする。
- 計測ループ中は GC を強制しない（本番のリーク機序を再現するため）。物理メモリ枯渇を防ぐ打ち切り上限ガード付き。
- 実行環境: x64 / .NET Framework 4.8 / 物理メモリ 16 GB。

## 結果 (Results)

### 条件A: IScam フルスケール（2440×1840・17ch・40 回）

| 指標 | 修正前（リーク） | 修正後（using） |
| --- | --- | --- |
| Private baseline | 0.05 GB | 0.05 GB |
| Private peak | **2.83 GB** | **0.62 GB** |
| 増分 (peak−base) | 2.78 GB | 0.57 GB |
| 1 回あたり平均増分 | **71 MB/回** | **15 MB/回** |
| 推移 | 0.82→1.49→2.16→2.83 GB と階段状に上昇 | 全 40 回 0.62 GB で一定 |

→ 修正後の増分は修正前の **約 20%**。

### 条件B: 小サイズ・多反復（900×700・17ch・60 回）

| 指標 | 修正前（リーク） | 修正後（using） |
| --- | --- | --- |
| Private peak | **0.53 GB** | **0.13 GB** |
| 増分 (peak−base) | 0.48 GB | 0.08 GB |
| 1 回あたり平均増分 | **8 MB/回** | **1 MB/回** |
| 推移 | 反復に伴い増加（0.16→0.44→0.53 GB） | 全 60 回 0.13 GB で一定 |

→ 修正後の増分は修正前の **約 17%**。

### 条件C: 連続撮影フロー再現（フルスケール・2440×1840・channels 4/10/17・8 フレーム）

`ContinuousShootMemoryBenchmark`（`BlockingCollection` の生産者-消費者・1 フレーム複数チャンネル変換・フレームバッファを合成フレームで構造再現）での計測。単体反復でなく「実機の連続撮影に近いフロー」での結果。

| 指標 | 修正前（リーク） | 修正後（using） |
| --- | --- | --- |
| Private peak | **2.60 GB** | **0.92 GB** |
| 増分 (peak−base) | 2.55 GB | 0.87 GB |
| **1 フレームあたり増分** | **326 MB/frame** | **112 MB/frame** |
| WorkingSet peak | 2.47 GB | 0.92 GB |

→ 修正後の増分は修正前の **約 34%**。修正前は 1 フレームごとに約 326 MB 積み上がる（フレーム数を増やせば打ち切り上限＝OOM へ）。修正後は約 0.9 GB で頭打ち（処理中の一時ピークで蓄積なし）。修正後の per-frame 112 MB は主に「保持中フレーム＋処理中の一時メモリ」で，反復しても天井は一定．

## 解釈 (Interpretation)

- **修正後は反復しても増えない**（peak の天井が一定＝蓄積ゼロ）。各呼び出しで中間 Mat が確定的に解放される。
- **修正前は反復に伴いメモリが増える**（1 回あたり数十 MB〜）。本番は IScam 17ch × 複数フレームを `SaveThread` で高速に回すため、ファイナライザの回収が追いつかず確保が先行 → OOM クラッシュに至る（PLAN_02 の症状）。
- 修正前のグラフが滑らかでなく**階段状/のこぎり波**になるのは、OpenCvSharp が GC にメモリ圧を通知し、未解放でも GC＋ファイナライザが周期的にネイティブメモリを一掃するため。見るべきは振れ幅でなく**ピーク包絡線のトレンド**（修正後＝水平、修正前＝上昇）。

## 限界・補足 (Limitations)

- 条件A/B は `ConvertImage` 単体の隔離計測。条件C は連続撮影フローを**構造再現**したもので、生産者-消費者スレッディング・多チャンネル・フレームバッファを含むが、**カメラ取得部のみ合成フレームに差し替えた再現**であり本番メソッド（`Shot`/`RunShotLoop`/`SaveThread`）そのものの呼び出しではない。実フレームレート・実カメラ・ファイル I/O（既定 OFF）までは含まない。実使用そのものの挙動は、`MEMMONITOR` ビルドのメモリ観測ウィンドウ＋実機撮影の録画で別途確認する（PLAN_02「効果検証（メモリ修正）の方法」参照）。
- 優先度5（Clear 時の未 Dispose）は未修正のため本対比の対象外（修正前/後どちらの変種にも同じだけ含まれ、差分には現れない）。

## 再現方法 (Reproduction)

```powershell
# テストプロジェクトを Debug|x64 でビルド後
$vstest = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe"
$dll    = "TIASshot.Tests\bin\x64\Debug\TIASshot.Tests.dll"
$env:BENCH_WIDTH="2440"; $env:BENCH_HEIGHT="1840"; $env:BENCH_CHANNEL="17"; $env:BENCH_FRAMES="40"; $env:BENCH_CEILING_GB="10"
& $vstest $dll /Platform:x64 /Framework:".NETFramework,Version=v4.8" /Tests:Compare_LeakyVsFixed_MemoryUsage /logger:"console;verbosity=normal"
```

※ スケールは環境変数 `BENCH_WIDTH/HEIGHT/CHANNEL/FRAMES/CEILING_GB` で調整可。
