2012-09-07 2 views
6

아래의 코드에서 잘못된 점을 알려줄 수 있습니까? 여기에는 전달 된 모든 객체의 XML 문자열을 반환해야하는 객체 직렬자가 있습니다.MemoryStream leak

우리는이 코드를 여러 번 호출하는 프로그램이 있고 우리의 메모리 사용량이 하늘 높이 올라간다는 것을 알았으므로 (프로그램이 완료된 후에도 거기에 머물러 있습니다.) 우리는이 코드를 통해 우리의 머리를 긁어 왔습니다. 검색은 끝났지 만 아무 소용이 없습니다. 스트림 객체는 "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); 
     } 
    } 

은 BTW 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

아마도 메모리가 부족하여 GC가 쉽게 사용할 수 있습니다. – ChaosPandion

+0

여기서 분명히 잘못된 것은 없습니다. 메모리 프로파일 러를 통해 실행 해 보았습니까? – Dervall

+0

예외가 발생 했습니까? serializer 호출에 스트림을 전달 중입니다. 그래서 나는 거기에 약간의 의문을 품고있다. 스트림 수명 시간이 using() 로케일을 훨씬 넘어서 확장된다는 것을 의미한다. – Zenwalker

답변

6

그 코드를 잘못 상당히 아무것도 없다; using이며 본질적으로이며,에 대해서는 no-op이며, 관리되는 자원은이며 관리되는 리소스는 GC의 도메인입니다. 그것은 GC가 약간의 메모리를 수집하기 위해 감지을 만들 때까지 GC가 크게 걱정하지 않는 것이 정상입니다. 그래서 나는 너무 많은 스트레스를주지 않을 것입니다. - 또는 : 문제가 있다면, 아마 그렇지 않을 것입니다.

한 관찰하지만, 당신은 인코딩 단계에서 버퍼를 피할 수 있다는 것입니다 :

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

을 실제로, 나는 기본적하지 ASCII로 UTF8을 사용하도록 유혹 할 것입니다.

최종 생각 : SerializerFactory자체가 누출되는 것을 수행하고 있습니까? 예를 들어, 보다 복잡한 생성자 중 하나를 통해 new XmlSerializer(...)을 생성하고 있습니까? 가장 단순한 형태 :

new XmlSerializer(typeof(SomeType)); 

는 미세 - 그것은 내부 캐시하는 내부 직렬은 당 유형 및 이러한 방법을 만들어 각 XmlSerializer 이것은 예를 들어 재 - 사용/실제. 그러나 이 아닌은보다 복잡한 생성자 오버로드에 대해이 캐싱을 수행합니다. 매번 새로운 동적 어셈블리를 만들고로드합니다. 그리고 이런 방식으로로드 된 어셈블리는 언로드되지 않습니다. - 예, 으로 인해 메모리 누수가 발생할 수 있습니다. 나는 이 실제 문제인지 확인하기 위해 serializer 인스턴스가 생성되는 것을보고 싶어합니다. 이러한 경우는 일반적으로 공장에서 자신의 시리얼 캐시를 작성하여 해결하기가 매우 용이합니다 :

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' 메쏘드에 serializer-cache를 추가하기 위해 편집했습니다. –

+0

HOLY CRAP ... 수정은 모든 마술을했습니다. 당신이 옳았다면, 우리는 아무것도 캐싱하지 않고 오래된 코드를 사용하여 우리 프로그램에 충격을주었습니다 ... 10 만 시간 안에 ... 우리는 동적으로 어셈블리를 생성했습니다. 이제 우리는 그 수정을 적용했고 모든 것이 완벽합니다. 감사!!! – user1307017

3

메모리 누수가 MemoryStream에 발생하지 않습니다, 사실 XmlSerializer에 발생합니다

"이것은 XmlSerializer 생성자의 오버로드는 동적으로 생성 된 어셈블리를 캐시하지 않지만 새 XmlSerializer를 인스턴스화 할 때마다 새 임시 어셈블리를 생성합니다. 앱에서 임시 어셈블리 형태로 관리되지 않는 메모리를 누출했습니다."

이 문서에 살펴 보자 : 각 유형에 대해 캐시 필요

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

대신 XmlSerializer 매번 만드는, 당신의 문제를 해결하기 위해.

+0

이것은 Marc 's와 비슷합니다. 그것은 효과가 있었고 우리는 오늘 새로운 것을 배웠습니다. – user1307017