2017-10-18 7 views
4

런타임시 OutOfMemoryException에 대한 자세한 정보를 얻을 수있는 방법이 있습니까? 또는,이 예외는 어떻게 든 둘러싸인 try/catch에 의해 잡히지 않고 대신 try/catch가 호출 스택을 높일 수 있습니까? WinDBG를 사용하여 재현 할 수 없으므로 응용 프로그램에서 로그 할 수 있어야합니다.System.Drawing.Bitmap에서 OutOfMemoryException이 발생했습니다.

나는 긴 설명을 사과하지만, 설명 할만한 많은 원인이있다.

OutofMemoryException에 대한 모든 가능성을 읽고 기본적으로 모두 제거했습니다. 일반적으로 응용 프로그램은 훌륭하게 실행되지만 경우에 따라 특정 컴퓨터에서만 OutOfMemoryException이 발생합니다. 이 보고서는 현지에서 재현 할 수없는 분야이기 때문에 갈 로그 만 있습니다. 그러나 나는 상당한 양의 세부 사항을 가지고있다.

이상한 무엇인가 논리적으로 주변에 메모리를 할당 할 수

  • 뭐든지 시도/캐치에 있지만 예외가 처리되지 않은으로 처리됩니다 (그리고 잡힌까지 더 높은 호출 스택)
  • 사용중인 StringBuffers가 없습니다.
  • 응용 프로그램을 다시 시작하고 다시 시작한 후에도 예외가 발생합니다.
  • 몇 분만 지나면 예외가 발생하고 약 30MBi의 메모리가 할당되어 1.5MiB 이하의 청크가됩니다.
  • 응용 프로그램 ("모든"프로세서 용으로 빌드 됨)이 64 비트로 실행되고 있음을 확인했습니다.
  • 디스크 공간 (270Gb 사용 가능)이 부족하지 않고 페이지 파일이 사용 가능합니다.
  • 은 가능한 LoH 단편화 문제가 아닌 것으로 보입니다.

최근 몇 차례이 응용 프로그램의 여러 부분에서 발생했습니다. 처음에는 System.Web.Serialization 어셈블리를 처음로드 할 때 예외가 발생했기 때문에 손상된 .NET 어셈블리가 있다고 결론을 내 렸습니다. 해당 어셈블리가 처음으로 사용 된 메서드 호출 중에 바로 발생하는 것으로 판단 할 수 있습니다. 컴퓨터를 원래대로 설정하고 Windows를 업데이트하면이 문제가 해결되었습니다.

그러나 며칠 내에 발생하는 두 번째 사례 인 다른 고객도 부패합니다. 이것은 어셈블리가로드되지 않는 위치에서 발생합니다. 나는 지금 첫 번째 오류를 재고하고있다. 알 수 있습니까 무엇 :

  • 그것은 스레드 풀 스레드에서 일어나고있는 (System.Timers.Timer는, [Statthread]) 활성 스레드 (< 5)의
  • 작은 수는
  • 그것은 시간 a를 주위에 발생
      1MiB 파일이 다운로드됩니다. 이것은 MemoryStream으로 읽혀 지므로 2MiB만큼 클 수 있습니다. 그런 다음 System.Drawing.Bitmap 생성자에 공급되어 비트 맵이 약 8MiB가됩니다. 그러나, 그것은 모두 System.Exception을 잡는 try/catch에 있습니다. try/catch에없는 유일한 것은 byte [] 참조를 반환하는 것이며,이 참조는 메모리 할당이 아닌 참조 사본이어야합니다.
    • 이 시간 전에 다른 중요한 메모리 할당이 수행되지 않았습니다. 동일하게 실행되어야하는 내 로컬 버전의 힙을 검토하면 응용 프로그램 아이콘과 Small Object Heap에있는 수십 개의 객체 만 표시됩니다.
    • 특정 입력이있는 특정 시스템에서 반복 가능합니다. 그러나 이러한 시스템은 서로 복제됩니다.분명한 차이점은 Windows 업데이트 순서뿐입니다.
    • 실행중인 어셈블리에 서명되어 있습니다. 체크섬이 손상되지 않았는지 확인하지 않습니까? 모든 시스템 어셈블리와 동일합니까? 이 인스턴스가 손상된 dll 또는 데이터로 설명 될 수있는 방법을 알지 못합니다.
    • 예외 시간에 호출 스택을 보는 것은 의외로 도움이되지 않습니다. 아래 코드에서 예외가 발생하는 위치를 나타냅니다.
    • 일부 COM 개체가 사용됩니다. 그러나, 정상적인 조건에서 우리는 메모리 문제없이 주 동안 응용 프로그램을 실행하고, 우리는 이러한 예외를 얻을 때, 그들은 단지 20 상대적으로 가벼운 COM 객체 (IUPnPDevice) 그래서

      // Stack Trace indicates this method is throwing the OutOfMemoryException 
      // It isn't CAUGHT here, though, so not in the try/catch. 
      // 
      internal void Render(int w, int h) 
      { 
          if (bitmap != null) 
          { 
           bitmap.Dispose(); 
           bitmap = null; 
          } 
      
          if (!String.IsNullOrEmpty(url)) 
          { 
          // this information is printed successfully to log, with correct url 
          // exception occurs sometime AFTER this somehow. 
           Logger.Default.LogInfo("Loading {0}", url); 
      
          // when file contents changed (to go from 1MiB to 500MiB, the error went away) 
           byte[] data = DownloadBinaryFile(url); 
           if (data != null) 
           { 
            try 
            { 
             Bitmap bmp; 
             using (var ms = new MemoryStream(data)) 
             { 
              bmp = new Bitmap(ms); 
             } 
             bitmap = bmp; 
            } 
            catch (Exception) 
            { 
             // We do not catch anything here. 
             Logger.Default.LogWarning("WARNING: Exception loading image {0}", url); 
            } 
           } 
      
           // 
           // if we had any errors, just skip this slide 
           // 
           if (bitmap == null) 
           { 
            return; 
           } 
      
           // QUESTION EDIT: 
           // in the problematic version, there was actually an unnecessary 
           // call here that turns out to be where the exception was raised: 
           using(Graphics g = Graphics.FromImage(bitmap)) { 
           } 
      
          } 
      } 
      
      // calling this would trigger loading of the System.Web assembly, except 
      // similar method has been called earlier that read everything. Only 
      // class being used first time is the BinaryReader, which is in System.IO 
      // and already loaded. 
      internal static byte[] DownloadBinaryFile(String strURL, int timeout = 30000) 
      { 
          try 
          { 
           HttpWebRequest myWebRequest = HttpWebRequest.Create(strURL) as HttpWebRequest; 
      
           myWebRequest.KeepAlive = true; 
           myWebRequest.Timeout = timeout; 
           myWebRequest.ReadWriteTimeout = timeout; 
           myWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"; 
      
           Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); 
      
           using (HttpWebResponse myWebResponse = myWebRequest.GetResponse() as HttpWebResponse) 
           { 
            if (myWebResponse.StatusCode != HttpStatusCode.OK) 
            { 
             Logger.Default.LogWarning("WARNING: Response {0} loading {1}", myWebResponse.StatusCode, strURL); 
             return null; 
            } 
      
            using (Stream receiveStream = myWebResponse.GetResponseStream()) 
            { 
             using (BinaryReader readStream = new BinaryReader(receiveStream)) 
             { 
              // this extension method uses MemoryStream, but seems irrelevant since we don't catch anything here. 
              return readStream.ReadAllBytes(); 
             } 
            } 
           } 
          } 
          catch (Exception e) 
          { 
           // we do not catch anything here. 
           Logger.Default.LogError("ERROR: Exception {0} loading {1}", e.Message, strURL); 
          } 
          return null; 
      } 
      

    사용 후 거의 즉각적 , 그 모든 후, 나는 개방 질문으로 돌아 간다. 내가 조사 할 수있는 OutOfMemoryException 객체에 대해 알려진 속성이 있거나 예외를 throw 한 후 호출하여이를 좁힐 수 있습니까?

    And .. OutOfMemoryException이 첫 번째 try/catch에 의해 포착되지는 않지만 호출 스택 위로 더 잡히게되는 이유는 무엇입니까?

  • +3

    바이트 배열 (데이터) 때문에 mem 조각화가 발생할 수 있습니다. DownloadBinaryFile이 스트림을 반환하도록합니다. 데이터 크기에 따라 MemoryStream 또는 FileStream을 반환하도록 선택할 수 있습니다. –

    +0

    스택 트레이스를 보여줄 수 있습니까? 이 문제는 조각화로 인해 발생할 수 있습니다. 가능한 해결 방법은 [https://stackoverflow.com/a/8564247/4684493](https://stackoverflow.com/a/8564247/4684493)을 참조하십시오. 또한 디버깅을 돕기 위해 [MemoryFailPoint] (https://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx)를 사용할 수도 있습니다. 결국 – Hintham

    +0

    이 동의하면 조각화가 발생할 수 있습니다. 그러나 이것은 첫 번째 또는 두 번째 큰 힙 할당에서 발생합니다. –

    답변

    4

    감사합니다. 대답은 다소 호기심이 많았고 알아 내기가 어려웠던 몇 가지 세부 사항이 잘못되었습니다. 오류는 여기에 있습니다 :

    당신은 비트 맵의 ​​수명 오픈 스트림을 유지해야합니다 : 비트 맵 생성자에 문서의 발언에

      Bitmap bmp; 
          using (var ms = new MemoryStream(data)) 
          { 
           bmp = new Bitmap(ms); 
          } 
          bitmap = bmp; 
    

    , 나는이 발견했다.

    분명히, 작성 직후에 MemoryStream을 닫는 것이이 정책을 위반 한 것입니다. 이 사이의 가비지 컬렉션과 실제로 비트 맵을 사용할 때 분명히 오류가 발생했습니다. (편집 : 사실, 그 경계는 1MStreamB 주위에 존재하는 것으로 보인다. FromStream 함수는 초기에 JPEG 파일의 상당 부분만을 압축 해제하는 것으로 보인다 .JPEG < 1MiB의 경우 전체 이미지가 압축 해제되고 초기화 후 스트림을 실제로 사용하지 않는다. . 더 큰 JPEG의 경우 첫 번째 1MiB 이상으로 픽셀이 필요할 때까지 읽지 않습니다.)

    Microsoft가 왜 이렇게했는지 상상하기 어렵습니다. 내 긴 좌절 검색을 주도 무엇

    // Create a Bitmap object from a file. 
    using (var ms = new MemoryStream(data)) 
    { 
        bmp = new Bitmap(ms); 
        Rectangle cloneRect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
        System.Drawing.Imaging.PixelFormat format = bmp.PixelFormat; 
        this.bitmap = bmp.Clone(cloneRect, bmp.PixelFormat);  
    } 
    

    과의 배제 : 나는 내가 볼 그래서 유일한 해결책은 비트 맵을 복제하는 것입니다 (HTTP 연결 인) 중 하나를 열어 원래의 스트림을 유지하고 싶지 않아요 정보의 핵심은 클라이언트 시스템에서 실행되는 코드가 미묘한 변화 만있는 약간 오래된 버전이라는 것입니다. Graphics.FromImage()는 이전 버전의 비트 맵에서 호출되었지만 제거되었습니다. 여전히, 그 버전은 대다수의 시간 동안 매우 잘 작동했습니다.