2012-09-07 7 views
6

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

これを複数回呼び出すプログラムがあり、私たちのメモリ使用量が高くなり(プログラムが終了してもそこにとどまっている)検索は完了しましたが、無駄になります。ストリームオブジェクトは "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; 
    } 
} 
+1

多分、メモリが圧迫されていない可能性があります。 – ChaosPandion

+0

ここに明らかに間違ったことはないようです。あなたはメモリプロファイラーでそれを実行しようとしましたか? – Dervall

+0

例外がスローされていますか?シリアライザ呼び出しにストリームを渡しています。だから私はそこに少し疑いを持っています、私はストリームライフタイムは、using()ロケールをはるかに超えて拡張されていることを意味します。 – Zenwalker

答えて

6

ありません。 usingリソースしか管理されていないため、MemoryStreamのノーオペレーションは本質的にであり、管理対象リソースはGCのドメインであることに注意してください。 センスがメモリを集めるまでGCが大いに心配しないようにするのは正常です。だから私はあまりストレスを感じません。もし問題があるなら、それはおそらくこれではありません。

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; 
     } 
    } 
} 
+0

は、SerializerFactoryを編集として追加しました。ありがとうございます。私たちはこれを見ていきます。 – user1307017

+0

@ user1307017あなたの 'Create'メソッドにシリアライザキャッシュを追加するように編集しました –

+0

HOLY CRAP ...すべての魔法を修正しました。あなたは正しかった、我々は何もキャッシュせずに古いコードを使って私たちのプログラムに衝撃を与えていた... 10万回のように..我々は動的にアセンブリを作成していた。ありがとう! – user1307017

3

メモリリークがMemoryStreamに発生しません、それは実際にXmlSerializerに起こる:

「このような場合は、通常の工場で独自のシリアライザ・キャッシュを作成することによって解決することは非常に簡単であることに注意してくださいXmlSerializerコンストラクタのオーバーロードは動的に生成されたアセンブリをキャッシュしませんが、新しいXmlSerializerをインスタンス化するたびに新しい一時アセンブリを生成します。アプリケーションは、一時的なアセンブリの形で管理されていないメモリをリークしています。」

は、この記事で見てみましょう:

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

代わりXmlSerializer毎回を作成する、あなたはあなたの問題を解決するために、各タイプのキャッシュする必要があります。

+0

説明のおかげで、これはMarc'sと似ています。それは働いて、今日何か新しいことを学んだ – user1307017