7

以下のコードの何が問題なのか、誰か教えてもらえますか? ここには、渡されたオブジェクトの XML 文字列を返すオブジェクト シリアライザーがあります。

これを複数回呼び出すプログラムがあり、メモリ使用量が非常に高くなる(そしてプログラムが完了した後もそこにとどまる)ため、これについて頭を悩ませてきました..検索を行いましたが、役立たず。ストリーム オブジェクトは "using" ステートメント内にあるため、これは単独で破棄されるはずだと考えていました。助けてください。

    public static string ToXML(this IMessage m)
    {          
        try
        {
            var serializer = SerializerFactory.Create(m.GetType());
            using (var stream = new MemoryStream())
            {
                serializer.Serialize(new[] { m }, stream);
                stream.Position = 0;
                var s = Encoding.ASCII.GetString(stream.ToArray());
                return s;
            }
        }
        catch (Exception e)
        {
            return string.Format("Message unserializable: {0}", e.Message);
        }
    }

ところで、SerializerFactory は次のようになります。

public class SerializerFactory
{
    public static IMessageSerializer Create(Type t)
    {
        var types = new List<Type> { t };
        var mapper = new MessageMapper();
        mapper.Initialize(types);
        var serializer = new XmlMessageSerializer(mapper);

        serializer.Initialize(types);

        return serializer;
    }
}
4

2 に答える 2

7

そのコードには大きな問題はありません。は管理されたリソースのみを持ち、管理されたリソースは GC のドメインであるため、using基本的に ではノーオペレーションであることに注意してください。ある程度のメモリを収集することが理にかなっているまで、GC があまり心配しないのは普通のことなので、あまり強調しません。または、問題がある場合は、おそらくそうではありません。MemoryStream

ただし、1 つの観察事項として、エンコード ステップでバッファーを回避できることが挙げられます。

var s = Encoding.ASCII.GetString(stream.GetBuffer(), 0, (int)stream.Length);

実際、ASCII ではなく、デフォルトで UTF8 を使用したくなるでしょう。

最終的な考え: あなたSerializerFactory 自身がリークするようなことをしていますか? たとえば、より複雑なコンストラクターnew XmlSerializer(...)のいずれかを介して作成していますか? 最も単純な形式:

new XmlSerializer(typeof(SomeType));

問題ありません-タイプごとに内部/実際のシリアライザーを内部的にキャッシュし、XmlSerializerこの方法で作成された各インスタンスに対してこれを再利用します。ただし、より複雑なコンストラクターのオーバーロードに対しては、このキャッシュは行われませ。毎回、新しい動的アセンブリが作成されて読み込まれます。そして、この方法でロードされたアセンブリはアンロードされることはありませんつまり、メモリ リークが発生する可能性があります。それが実際の問題であるかどうかを確認するために、シリアライザーインスタンスがどのように作成されるかを非常に知りたいと思います。このようなケースは通常、ファクトリで独自のシリアライザー キャッシュを作成することで簡単に修正できることに注意してください。

public class SerializerFactory
{
    // hashtable has better threading semantics than dictionary, honest!
    private static readonly Hashtable cache = new Hashtable();
    public static IMessageSerializer Create(Type t)
    {
        var found = (IMessageSerializer)cache[t];
        if(found != null) return found;

        lock(cache)
        {   // double-checked
            found = (IMessageSerializer)cache[t];
            if(found != null) return found;

            var types = new List<Type> { t };
            var mapper = new MessageMapper();
            mapper.Initialize(types);
            var serializer = new XmlMessageSerializer(mapper);

            serializer.Initialize(types);

            cache[t] = serializer;

            return serializer;
        }
    }
}
于 2012-09-07T06:51:50.103 に答える
3

メモリリークは発生しませんMemoryStream、実際には発生しXmlSerializerます:

「XmlSerializerコンストラクターのこのオーバーロードは、動的に生成されたアセンブリをキャッシュしませんが、新しいXmlSerializerをインスタンス化するたびに、新しい一時的なアセンブリを生成します。アプリは一時的なアセンブリの形でアンマネージメモリをリークしています。」</p>

この記事を見てください:

http://msdn.microsoft.com/en-us/magazine/cc163491.aspx

問題を解決するには、毎回作成するのではなく、タイプごとにキャッシュXmlSerializerする必要があります。

于 2012-09-07T06:57:04.063 に答える