メモリリークのない C# の実行

アプリケーションドメイン

前回、「PowerShell で C# の実行」では、メモリ内に動的アセンブリをコンパイルする方法をご紹介しました。しかし、この方法では、PowerShell を終了するまで解放できない動的アセンブリがメモリ内に次々に増えていくことを問題点として挙げました。今回ご紹介するのは、アプリケーションドメインを使ってアセンブリを解放する方法です。アプリケーションドメインについての詳細は、以下をご覧ください。

 

アプリケーションドメインを使う

.NET のプロセスは、1 つ以上のアプリケーションドメインで成り立っていて、アセンブリは、いずれかのアプリケーションドメイン内にロードされます。アセンブリだけを単独で解放する手段はありませんが、アプリケーションドメインを解放することで、そこに読み込まれたアセンブリが解放されます。そこで、新しくアプリケーションドメインを作成し、そこにコンパイル済みのアセンブリを読み込んで実行し、終了後にアプリケーションドメインを解放する、という方法を採ることにします。

 

ダウンロード

以下より、メモリリーク無しで C# を実行するスクリプトをダウンロードできます。

 

用例

前回の用例がそのまま使えますので、ご覧ください。ただし、前回の Invoke-Cs.ps1 では可能であった、「新しい型を作成して使用すること」が、今回の Invoke-CsRemote.ps1 ではできなくなっています。

用例: 新しい型の作成

PS > Get-Content hello.cs
class Program {
    static object Main()
    {
        return new Program();
    }
    public string Hello()
    {
        return "Hello World!";
    }
}
PS > $a = Invoke-Cs hello.cs
PS > $a.Hello()
Hello World!
    ※ Invoke-Cs では成功
PS > $a = Invoke-CsRemote hello.cs
"5" 個の引数を指定して "InvokeMember" を呼び出し中に例外が発生しました: "呼び出しのターゲットが例外をスローしました。" 発生場所 Invoke-CsRemote.ps1:70 文字:57 +         $result = [Runtime.Remoting.ObjectHandle].InvokeMember( <<<< ‘Unwrap’, ‘InvokeMethod’, $null, $objectHandle, $null)
    ※ Invoke-CsRemote では失敗

これはなぜかと言うと、新しい型を作成したアセンブリが別のアプリケーションドメインにあるために、型情報を読み込むことができないからです。

Invoke-Cs と Invoke-CsRemote の使い分け

Invoke-Cs は、 PowerShell を終了するまでアセンブリを保持し続けます。これはメモリリークの原因となりますので、できる限り Invoke-CsRemote を使うのが良いでしょう。ただし、新しい型を作る必要がある場合には Invoke-Cs のような方法を使わざるをえません。その場合でも、作ったアセンブリをその度に使い捨てにするのではなく、一度作成した型を何度も再利用することで、メモリリークを防ぐことができます。また、頻繁に同じ C# ソースを実行するようであれば、恒常的なアセンブリにコンパイルすることを検討すべきかもしれません。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です