0

OK、C# の winform アプリを作成しました。これは File_Splitter_Joiner です。ファイルを渡すだけで、指定した数の断片に分割されます。分割は別のスレッドで行われます。1Gig ファイルをスライスするまで、すべてがうまく機能していました。タスク マネージャーで、私のプログラムが 1 ギガバイトのメモリを消費し始め、コンピューターが死にかけているのを見ました! それだけでなく、スライスが終わったとき、消費は変わりませんでした!(これがガベージコレクタが機能していないことを意味するかどうかはわかりませんが、ビッグデータチャンプを保持していたものへのすべての参照を失ったことはかなり確信しているので、機能するはずです)考え):

public FileSplitter(string FileToSplitPath, string PiecesFolder, int NumberOfPieces, int PieceSize, SplittingMethod Method)
{
  FileToSplitInfo = new FileInfo(FileToSplitPath);
  this.FileToSplitPath = FileToSplitPath;
  this.PiecesFolder = PiecesFolder;
  this.NumberOfPieces = NumberOfPieces;
  this.PieceSize = PieceSize;
  this.Method = Method;
  SplitterThread = new Thread(Split);
}

そして、実際の分割を行った方法は次のとおりです: (私はまだ初心者なので、これから説明することは可能な限り最良の方法で行われない可能性があります。私はここで学んでいます)

private void Split()
{
  int remainingSize = 0;
  int remainingPos = -1;
  bool isNumberOfPiecesEqualInSize = true;
  int fileSize = (int)FileToSplitInfo.Length; // FileToSplitInfo is a FileInfo object
  if (fileSize % PieceSize != 0)
  {
    remainingSize = fileSize % PieceSize;
    remainingPos = fileSize - remainingSize;
    isNumberOfPiecesEqualInSize = false;
  }
  byte[] fileBytes = new byte[fileSize];
  var _fs = File.Open(FileToSplitPath, FileMode.Open);
  BinaryReader br = new BinaryReader(_fs);
  br.Read(fileBytes, 0, fileSize);
  br.Close();
  _fs.Close();

  for (int i = 0, index = 0; i < NumberOfPieces; i++, index += PieceSize)
  {
   var fs = File.Create(PiecesFolder + "\\" + Path.GetFileName(FileToSplitPath) + "." + (i+1).ToString());
   var bw = new BinaryWriter(fs);
   bw.Write(fileBytes, index, PieceSize);
   if(i == NumberOfPieces-1 && !isNumberOfPiecesEqualInSize && Method == SplittingMethod.NumberOfPieces)
   bw.Write(fileBytes, remainingPos, remainingSize);
   bw.Close();
   fs.Close();
  }
 MessageBox.Show("File has been splitted successfully!");
 SplitterThread.Abort();
}

ここで、BinaryReader を介してファイルのバイトを読み取る代わりに、最初にFile.ReadAllBytesメソッドを介してファイルを読み取っていました。小さなファイル サイズでは問題なく動作していましたが、私たちの大物を扱ったときに「SystemOutOfMemory」例外が発生しました。 BinaryReader を介してバイトを読み取ったときに、なぜその例外が発生しなかったのか。

(それは中間の質問でした)

それで、主な質問は、メモリをそれほど消費しない方法で大きなファイルをロードするにはどうすればよいかということです(ギグと言えば)?つまり、プログラムがそのメモリをすべて消費しないようにするにはどうすればよいでしょうか? 分割が完了した後、使用済みメモリを解放するにはどうすればよいですか? (実際に使った

bw.Dispose; fs.Dispose; 

それ以外の

bw.Close(); fs.Close(); 

それは同じでした。Q が意味をなさない可能性があることはわかっています。何かをロードすると、他の場所ではなくメモリに保存されますが、そのように尋ねた理由は、別の Splitting_Joining プログラム (私が作成したものではありません) を使用したためです。同じ問題が発生した場合、ファイルをロードすると、プログラムは約5MigsのRAMを消費し、分割を開始すると約10Migsを使用しました!! これは非常に大きな違いです..おそらくそのアプリはC / C ++でした..

要約すると、誰が悪いのですか?それは私のコードですか?もしそうなら、どうすれば修正できますか? それともパフォーマンスに関してはC#ですか?

あなたが私をつなぐことができるものをすべてありがとうございます:)

4

2 に答える 2

2

次の2行はあなたを殺します:

int fileSize = (int)FileToSplitInfo.Length; // a FileInfo object
...
byte[] fileBytes = new byte[fileSize];
  1. サイズがオーバーすると、コードは失敗しInt32.MaxValueます。不要、使うだけlong fileSize = FileToSplitInfo.Length;
  2. この修正されたコードは、十分な連続メモリがない場合に失敗します。(LOH の) 断片化は、遅かれ早かれあなたをダウンさせます。
  3. ファイル全体にメモリを割り当てますが、一度に必要なのはPieceSizeバイトだけです。

fileSize を知る必要さえありません。

byte[] pieceBuffer = new byte[PieceSize];

while (true)
{
    int nBytes = br.Read(pieceBuffer, 0, pieceBuffer.Length);
    if (nBytes == 0) 
       break;

    // write this piece, the length is nBytes 
}
于 2012-09-06T21:58:53.463 に答える
1

改善できるさまざまな側面があります。

  • 大きなファイルで作業している場合、なぜ最初に配列内のすべてを読み取り、別のファイルに書き込んだ後ですか? 他のファイルから読み取りながら、新しいファイルに書き込むだけです。

  • いずれの場合でも、ストリームの破棄を保証するために使用usingします: 例外があるかどうか。

  • 1GB 以上の非常に大きなファイルを扱う場合は、 Memory Mapped Filesを参照することをお勧めします。そのため、パフォーマンス コストがいくらか増加しますが、驚くほどのメモリ消費の利点が平均化されます。

于 2012-09-06T21:56:07.147 に答える