2017-10-18 39 views
4

実行時にOutOfMemoryExceptionについて詳細を取得する方法はありますか?または、この例外は、どういうわけか、囲みtry/catchによってキャッチされず、代わりにtryスタック/ catchが呼び出しスタックを上回る可能性がありますか?私はWinDBGを使って再現することができないので、アプリケーションからログできるものでなければなりません。System.Drawing.BitmapのC#OutOfMemoryException

私は長い説明をお詫び申し上げますが、説明するために考えられる原因はたくさんあります。

私はOutofMemoryExceptionのすべての可能性について読んで、基本的にすべてを取り除いています。通常、アプリケーションはうまく動作しますが、時には特定のコンピュータのみで、OutOfMemoryExceptionが発生します。これらのレポートは現場では再現性がないため、私はログを残すだけです。しかし、私はかなりの細部を持っています。

は奇妙な何かである:論理的に近くにメモリを割り当てることがあり

  • 何でものtry/catchであるが、例外が未処理として扱われます(とキャッチアップはるかに高いコールスタック)
  • StringBuffersが使用されていません
  • アプリケーションを再起動して再起動しても例外が発生します。
  • 例外はわずか数分後に発生し、約30MiBのメモリしか割り当てられません。チャンクでは1.5MiBを超えません。
  • アプリケーション(「任意の」プロセッサ用に構築された)が64ビットとして動作していることを確認しました。
  • ディスクスペース(270Gbフリー)とページファイルが不足していません。
  • は、LoHフラグメンテーションの問題ではないようです。

これは最近、アプリケーションのさまざまな部分で数回発生しました。初めて、System.Web.Serializationアセンブリを最初にロードするときに例外が発生していたため、.NETアセンブリが破損していると判断しました。そのアセンブリが最初に使用されたメソッド呼び出しの間に起こっていたと判断できました。コンピュータの再イメージング(元のセットアップと同じ)とウィンドウの更新は、この問題を解決しました。

しかし、数日以内に起きる別のクライアント(別のクライアント)も腐敗している可能性は非常に低いです。これは、アセンブリがロードされない場所で発生しています。私は今、最初のエラーを再考しています。私は知っていますか:

  • をそれは、スレッドプールのスレッドで起こっている(でSystem.Timers.Timer、[Statthread])
  • それは時間Aの周りに起こる(< 5)
  • アクティブなスレッドの数が少ないです1MiBファイルがダウンロードされます。これはMemoryStreamに読み込まれるため、2MiBほどの大きさになる可能性があります。これはSystem.Drawing.Bitmapコンストラクタに渡され、ビットマップは約8MiBになります。しかし、それはSystem.Exceptionをキャッチするtry/catchのすべてです。 try/catchに含まれていない唯一のことは、byte []参照を返すことです。この参照は、メモリの割り当てではなく参照コピーでなければなりません。
  • この前に他の重要なメモリ割り当ては行われていません。同じように動作するローカルバージョンのヒープを調べると、小さなアイコンヒープ上にあるアプリケーションアイコンと数十のオブジェクトだけが表示されます。
  • 特定のシステムで特定の入力を繰り返してもかまいません。しかし、これらのシステムは互いにクローン化されています。明らかな違いは、ウィンドウの更新の順序だけです。
  • 実行中のアセンブリに署名があります。それが壊れていないことを保証するチェックサムはありませんか?すべてのシステムアセンブリで同じですか?私は、このインスタンスが破損した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オブジェクトに既知のプロパティがありますか、またはこれを絞り込むために例外がスローされた後に呼び出すことができるコールはありますか?そこでOutOfMemoryExceptionが最初のtry/catchによってキャッチされないだろうが、コールスタックまで、さらにキャッチされるだろう、何らかの理由

And..isを?

+3

あなたはので、バイト配列(データ)のMEM断片化を期待することができます。 DownloadBinaryFileがストリームを返すようにします。データサイズに応じて、MemoryStreamまたはFileStreamを返すように選択できます。 –

+0

スタックトレースを表示できますか?可能性のある回避策については、[https://stackoverflow.com/a/8564247/4684493](https://stackoverflow.com/a/8564247/4684493)を参照してください。また、あなたはおそらく、デバッグ – Hintham

+0

が合意し、最終的に断片化があるかもしれないあなたを助けるために、[MemoryFailPoint](https://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx)を使用することができます。しかし、これは第1または第2の大きなヒープ割り当てで発生しています。 –

答えて

4

はあなたのすべてをありがとうございました。答えはやや好奇心が強いですし、いくつかの細部は間違っていました。エラーはここにある:あなたがビットマップの寿命のためのオープンストリームを維持する必要があります

:ビットマップコンストラクタのマニュアルの発言で

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

、私はこれを見つけました。

明らかに、構築直後にMemoryStreamを閉じることはこれに違反していました。これと実際にビットマップを使用したときの間のガベージコレクションでは、明らかにエラーが発生していました。 (編集:実際は、1Mバイトの周りに境界が存在し、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()は以前のバージョンのビットマップで呼び出されましたが、削除されました。それでも、そのバージョンは時間の大部分を非常によく機能させました。