2016-10-06 14 views
-4

ユーザーはファイル名とブロックサイズを指定します。元のファイルは、ユーザーブロックサイズ(最後のブロックを除く)のブロックに分割されます。各ブロックについて、ハッシュ関数SHA256を計算し、コンソールに書き込みます。配列のメモリリークc#

これは2つのスレッドを持つプログラムです。最初のスレッドは元のファイルを読み込み、ブロックのバイト配列をキューに入れます。 2番目のスレッドは、ブロックのバイト配列をキューから削除し、ハッシュを計算します。

最初の反復メモリは、プログラムが完了するまで処理されません。
次回の反復では、メモリが正常に割り当てられ、処分されます。
したがって、次の部分配列の読み込みでは、OutOfMemoryExceptionが得られます。

メモリリークを防ぐためにメモリを正しく管理するにはどうすればよいですか?

class Encryption 
{ 
    static FileInfo originalFile; 
    static long partSize = 0; 
    static long lastPartSize = 0; 
    static long numParts = 0; 
    static int lastPartNumber = 0; 
    static string[] hash; 
    static Queue<byte[]> partQueue = new Queue<byte[]>(); 

    public Encryption(string _filename, long _partSize) 
    { 
     try 
     { 
      originalFile = new FileInfo(@_filename); 
      partSize = _partSize; 

      numParts = originalFile.Length/partSize; 
      lastPartSize = originalFile.Length % partSize; 

      if (lastPartSize != 0) 
      { 
       numParts++; 
      } 
      else if (lastPartSize == 0) 
      { 
       lastPartSize = partSize; 
      } 

      lastPartNumber = (int)numParts - 1; 

      hash = new string[numParts]; 
     } 
     catch (FileNotFoundException fe) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
      return; 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
     } 
    } 

    private void readFromFile() 
    { 
     try 
     { 
      using (FileStream fs = new FileStream(originalFile.FullName, FileMode.Open, FileAccess.Read)) 
      { 
       for (int i = 0; i < numParts; i++) 
       {       
        long len = 0; 

        if (i == lastPartNumber) 
        { 
         len = lastPartSize; 
        } 
        else 
        { 
         len = partSize; 
        }        

        byte[] part = new byte[len];     

        fs.Read(part, 0, (int)len); 

        partQueue.Enqueue(part); 

        part = null; 
       } 
      } 
     } 
     catch(Exception e) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
     }  
    } 

    private static void hashToArray() 
    { 
     try 
     { 
      SHA256Managed sha256HashString = new SHA256Managed(); 
      int numPart = 0; 

      while (numPart < numParts) 
      { 
       long len = 0; 
       if (numPart == lastPartNumber) 
       { 
        len = lastPartSize; 
       } 
       else 
       { 
        len = partSize; 
       } 

       hash[numPart] = sha256HashString.ComputeHash(partQueue.Dequeue()).ToString(); 

       numPart++; 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
     } 
    } 

    private void hashWrite() 
    { 
     try 
     { 
      Console.WriteLine("\nResult:\n");     
      for (int i = 0; i < numParts; i++) 
      { 
       Console.WriteLine("{0} : {1}", i, hash[i]); 
      } 
     } 
     catch(Exception e) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
     } 
    } 

    public void threadsControl() 
    { 
     try 
     { 
      Thread readingThread = new Thread(readFromFile); 
      Thread calculateThread = new Thread(hashToArray); 

      readingThread.Start(); 
      calculateThread.Start(); 

      readingThread.Join(); 
      calculateThread.Join(); 

      hashWrite(); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace); 
     } 
    } 
} 
+0

あなたの質問は何ですか? –

+0

なぜ最初の反復メモリは処分されないのですか?そして、これを解決する方法は? –

+0

私はあなたの質問を理解するか分からない。また、スレッドを手動で処理する代わりに、[タスク](https://msdn.microsoft.com/en-us/library/dd537609(v = vs.110).aspx)でこれらをラップするのはなぜですか? –

答えて

0

このようなコードを書く前に、.NET内部についてのいくつかの書籍を読む必要があります。 .NETメモリモデルのあなたの理解は完全に間違っている、これはなぜこのようなエラーを取得します。 OutOfMemoryExceptionはごくまれに発生します。特に、あなたが配列を扱っている場合は、あなたのリソースについて気にする必要があります。

あなたは、.NETランタイムで参照オブジェクト、基本的なもの、とLarge Objects Heapのための2つのヒープがあることを知っている必要があり、それらの間の最も重要な違いは、LOHもガベージコレクション後に圧縮されていないということです。

小さなアレイでも、すべてのアレイがLOHに向かい、メモリが非常に消費されていることがわかります。 immidiately

part = null; 

doesn't dispose memory:また、あなたは、この行を知っている必要があります。さらに悪いことに、この行は、何もしません。なぜなら、あなたはまだキューに入っているファイルの部分を参照しているからです。これがあなたの記憶が消え去る理由です。各ハッシュ計算後にGCを呼び出すことでこれを解決できますが、これはであり、非常に推奨されない解決策ですです。

ファイル全体の内容を同時にメモリに保存せずにアルゴリズムを書き直す必要があります(これは非常に単純なパターンのProducer/Consumerです)。これは非常に簡単です。単に変数partを静的フィールドに移動し、次のファイル部分を読み込みます。キューの代わりにコード内にEventWaitHandle(またはone of it's child classes)を導入し、ファイルの次の部分を読んだ直後に次のハッシュを計算するだけです。

great series of Joe Albahariを読むことで、C#のスレッド化の基本から始めて、そのようなソリューションを実装しようとするとよいでしょう。あなたのプロジェクトに幸運。