2013年9月29日日曜日

[.NET] WebBrowserをDisposeしても、メモリが開放されない件について

そもそも、WebBrowserがメモリリークを起こす件は、随分前からいわれ続けている気がしますが、未だに解決されていないようです。
とりあえず、試しにWebBrowserを貼り付けたフォームを作って、適当なコンテンツを表示するようにしたものを、アプリケーションのメインウィンドウから、Show()で表示させて、そのフォームを閉じるようにしてみましたが、やはりリークが発生しているようです。

いろいろ検索したところによると、Win32APIのSetProcessWorkingSetSize()やEmptyWorkingSet()の利用が効果があるとのことですが、どうやら、これらも機能しないようです。ただ、OSやIEのバージョン、WebBrowserに表示した中味などの条件で機能することもあるようです。
この手の検索をしても、かなりの量がヒットしているところを見ると、やはり、今現在でも決定的な解決方法はないのでは、と思っています。

何がメモリに留まり続けているかというのを調べてみたのですが、パフォーマンスモニタで見てみると、.NET CLR Interopの# of CCWsという項目が、増え続けています。これによって、Bytes in all HeapsやTotal committed Bytesなども増え続けているように見えます。CCWというのは、COMのラッパーのことでなので、CLRから見るとCOMが開放されない状態になっているのではないかと思われます。
WebBrowserがIEの機能をCOMとして利用しているというのは、容易に想像できますが、具体的に何をどう使っているのかはわかりません(本気で追求すればわかりかもしれませんが、とりあえずなので、すみませんw)。
この現象からすると、根本的解決を試みるのであれば、開放されないCOMに対して何か操作するということぐらいしか思いつきません。

そういう状況から考えると、WebBrowserはDisposeするような使い方はしないというのが、無難な解決方法(というか、回避方法)ではないかと、思います。例えば、上記の書いたようなフォームにWebBrowserを貼り付けて表示するのであれば、Show()で表示するのではなく、別プロセスでフォームを表示させるという方法です。プロセスが終了すれば、無条件にメモリは開放されるので、これならリークしようがないはずです。
もし、メインのウィンドウとなんだかの情報のやり取りが必要であれば、IPCが使えます(と、あっさりいいましたが、IPCも曲者でタイムアウトしたり、やたらに遅かったりする場合がある)。

というわけで、もし、WebBrowserのメモリリークでお悩みの方は、どちらかというと残念な解決方法ですが、上記の方法を検討してみたらいかがでしょうか。