LoginSignup
26
29

More than 3 years have passed since last update.

de:code 2019 [DT03] 上級サポート エンジニアの経験お伝えします:Visual Studio 2019 でメモリー リークを追え!

Last updated at Posted at 2019-06-05

Microsoft de:code 2019に参加した内容を紹介します。

もし間違いなどあれば、ご指摘いただけますと助かります。

[DT03] 上級サポート エンジニアの経験お伝えします:Visual Studio 2019 でメモリー リークを追え! / 2019年5月29日

image.png

TL;DR(要約)

  • メモリリークといっても、エラー(「コンピューターのメモリが不足しています」、「OutOfMemory」)と遅延(システムやアプリケーションが「なんだがわからないが動かない」)の2種類がある。
  • 原因調査のためには、パフォーマンスカウンターで「Private BytesとVirtual Bytes とWorkingsets」を調べる。
  • 大幅な高速化とメモリ使用量が改善したVisual Studio 2019の診断ツールを使用することで、どのイベント(メソッド)でメモリリークが起きているかを追いかけることができる。

リソース

講師はどのような方?

vb4.0からサポートを行ってきた人、サポートを20年以上!

今日持ち帰っていただきたいこと

image.png

  • メモリリークって何?
  • メモリーリークが起きる「前に」しておくこと
  • メモリーリークが起きたらすること
  • Visual Studioもやるじゃん!

関連資料

image.png

メモリーリークが発生しているというエラーは見たことがある?

メモリーリークといいますけど、メモリーリークが発生しています!というエラーは起きないですよね。

では、実際にはどういったエラーを見るのか?

コンピューターのメモリが不足しています

それは、「コンピューターのメモリが不足しています」というメッセージ!

image.png

OutOfMemory!

または、OutOfMemoryExceptionのエラーが表示される。

image.png

なんだがわからないが動かない

または、なんだがわからないが動いていない、といわれる。アプリケーションの遅延、システム全体の遅延が発生する。

image.png

では、どうやって解決していくのか

実際に起きてみてわかることだが、そもそもメモリー使用量とはなんなのか?

問い合わせをもらうと、「まず最初にいつもはどうですか、普通はどうなのか」と聞くんですけど、普通って何か、ということがわからないこともよくある

  • そもそも普通というのはどう考えればよいのか
  • メモリー使用量内訳はどうやって調べればいいのか
  • 怪しいところをどうやって見つけるのか

こういったシナリオをベースに話していく

実際に試してみよう

新しいプロジェクト、Visual Studio 2019でC#のプロジェクトで試してみる。

今回は意図的に!

内容

  • static dictionaryを用意
  • ボタンクリックを契機に、byte配列を作成したdirectoryに追加。Guidのランダムキーで100MB程度追加するだけのコード

実践

連打すると OutOfMemory Exceptionが発生した。

【お題】C# で8GBメモリのマシンでOutOfMemoryが発生した。どこまで増やせばよいか?

分析

実際に見てみると、16MBしか使っていない???

なぜか?

Windowsは仮想メモリーを使用している。8GB+スワップファイル24GBで仮想メモリとなっている。

各プロセスのメモリ空間は次のようになっている

プロセスの種類 メモリ空間上限
32bitOS 2GB
64bitOS 32bitプロセス 4GB
64bitOS 64bitプロセス 8TB

プロセスが使用しているメモリ空間が多くても、実際に物理メモリを使っているのは数MBとなる。使用しているメモリをメモリ仮想メモリのどちらに置くかはOSが制御している。

コンピュータのメモリが不足していますというウインドウは、物理+仮想も足りなくなった場合!

image.png

しかし、今回はOutOfMemory!

image.png

解決方法

ではサポートが伝えるのはどういうところかというと、

パフォーマンスカウンターで「Private BytesとVirtual Bytes とWorkingsets」を調べてください

この3つはprocessのオブジェクトグループにある。しかも対象のプロセスを選ぶことができる

グラフのスケールを調整(リセット)し、Private Bytesを見ると3GB使っていることが分かった。
→ということは、Processのメモリ空間が足りなくなってOutOfMemoryが発生したということがわかる

名称 意味
Private Bytes メモリ空間の使用量
Virtual Bytes メモリ空間の予約量と使用量
Workingsets 物理メモリで実際に使用しているサイズ

タスクスケジューラでも、ワーキングセットとコミットサイズ(private bytes)を見ることができる

答え

一般的にプロセスのメモリー空間不足が原因。
システム全体の不足ではありません。
物理メモリーが足りなくなれば、システム全体に影響が出る。

【お題】C#で関数を抜けたのにプライベート関数のメモリーが解放されません。バグですか

分析

100MB追加し、100MB取り除くというコードを書いてみた。

知識

.NETではメモリーを大きく予約し(セグメント)、ちょっとずつ使います。

予約するセグメントの大きさ

OS 予約セグメントのサイズ
32bit 16MB~64MB
64bit 128MB~4GB

ガベージコレクタの動作

  • すべてのオブジェクトはいったん不要なものとみなし、参照されていないものを捨てる
  • ガベージコレクタは自律的に動く
  • ガベージコレクタは、世代管理する
  • ガベージコレクタが不要なオブジェクトを片付けていく。
  • ファイナライザは管理されていないオブジェクト(案マネージドリソース)を片付けるもの。ファイナライザが片付けて、再度ガベージコレクタしてやっと捨てられることになる

回答

ガベージコレクタは気分次第(いつ動くかどうかはわからない)

なので、OSが良きに計らうのでそのままとしておくのがよい。

GC.Collectを行うということはアプリケーションの通常動作としてやってはいけない。ガベージコレクタが動作すること自体に、コストがかかる(全スレッドを停止する)ため。
ただし、デバッグ用(本当に使用しているメモリーを知るためのみ)にそういう機能を裏で設ける、ということは有効な方法である。

【お題】プロセスがどんなオブジェクトでメモリーを使用しているか知りたいです。どんな方法がありますか

回答

昔からの方法:ダンプファイルをとりましょう!
今なら:Visual Studioのパフォーマンスプロファイラを使いましょう!

説明

プロセスのメモリ空間について

プロセスのメモリ空間には、exe、dllなどの アセンブリ、ヒープメモリ、スレッド、heap、 managed heap、Managed Threadなどがある。

調査方法

ダンプ出力

procdump.exe -ma -a -r (調査対象).exe c:\(ダンプファイル名).dmp

-ma -a -r。このオプションを追加することで、プロセスが止まらない。

出力したファイルをどうやって解析するか

方法1:昔ながらの方法(WinDbgで解析する)

  • アドレスを順番に解析していくと、Dictionaryで参照しているというところまで見ることができる
  • Dictionaryのサイズについても確認することができる。
  • サイズについても確認することができる
  • 原因となっているものの特定方法
    • 全体から詳細へとし、大きいものを探す
    • 特定のセグメント、包含するオブジェクトが多いもの(DtaSet, DataTable HashTable)
    • それでもわからないなら数が多いもの

問題点

サイズが大きいことはわかるが、なぜ多くなったのか、はわからない。
すでにリークしていれば、オブジェクトの数は膨大であり、GC待ちのものも存在する(このために、GC.Collectを行う裏スイッチを設けておく、という方法がある)

今ではお勧めしない

WinDbgでのダンプ解析は30分かかるが、時間泥棒な割に実入りが少ない。
昔は見たけど今はお勧めしない


現在のやり方

方法2:Visual Studioでやろう!

  1. Visual Studioにドラッグアンドドロップ(テキストエディタ部分)
  2. マネージドメモリをクリックするとヒープを分析
  3. 右上のマイコードのみのチェックを外すと、呪文を唱えなくても確認することができる
  4. 静的変数のform1.dというところまでわかる!!

方法3:Visual Studioの診断ツール!

あまり使い道がわかっていない人も多いと思うが、意外と役に立つやつである!

ダンプとは異なるが、スナップショットを作成することができる。

実際のデモ

  1. 処理ごとにスナップショットを取ってみる(メモリ使用量タブ)
  2. すると、スナップショットごとにどのくらい変わったということがわかる。例えば、オブジェクトの数が3個なのに大きい!
  3. ヒープサイズのところをクリックすると、内容が見ることができる。そして、比較対象として、スナップショット同士を比較することができる

デバッグ実行しながら、スナップショットの比較を行うことができる

方法4:デバッグからパフォーマンスプロファイラを呼び出す

「デバッグ実行って重いんでしょ?運用に入っているんだけど。。。」という疑問に対する回答となる。

  • プロファイラメソッドで対象を観測することができる。GCの強制もできる
  • 取集を止めると比較することができる

後は、方法3と同じような形で比較ができる

ではどこでおこったのか?

割り当て追跡を使用する。どのイベント(メソッド)で確保されたのかまでツリーを展開して追いかけることができる

補足

Visual Studio自体が非常に軽くなったことで、使えるものとなった。

image.png

Visual Studio 2019になってから、Visual Studko 2019のメモリ使用量がだいぶ減った。

~Visual Studio 2017では、3GBのダンプファイルを食わせると死んだりしていたので、実質使用に耐えなかった。今は、3GBのダンプファイルを食わせても、300MBしか使用しない。

要は、Visual Studio 2019でメモリ使用量が減ったことで現実的に使えるようになってきた。

まとめ

image.png

メモリーリーク

仮想メモリ、メモリ空間、マネージドメモリー、GC

メモリーリークが起きる「前に」しておくこと

obuservablitlity、GCキックを仕込んでおく(観測のために)、情報採取方法を確認しておく

メモリーリークが起きたらすること

タスクマネージャー、パフォーマンスカウンター、プロファイル


新しい時代に、新しいツールを!

26
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
29