【问题标题】:Issue with memory management and program performance内存管理和程序性能问题
【发布时间】:2012-09-06 21:50:14
【问题描述】:

好的,我制作了一个 C# winform 应用程序,它是一个 File_Splitter_Joiner。 您只需给它一个文件,它就会为您将其拆分为您指定的多个部分。 拆分在单独的线程中完成。 在我切片 1Gig 文件之前,一切都运行良好! 在任务管理器中,我看到我的程序开始消耗 1G 内存,我的电脑差点死机! 不仅如此,切片完成后,消耗并没有改变! (不知道这是否意味着垃圾收集器不工作,虽然我很确定我丢失了对持有大数据块的所有引用,所以它应该工作) 这是 Splitter 构造函数(只是为了给你一个更好的主意):

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 的内存,当我开始拆分时,它使用了大约 10Migs ! 现在这是一个非常大的区别.. 可能那个应用程序是 C/C++ ..

总而言之,谁很烂?它是我的代码吗?如果是,我该如何解决?还是在性能方面是 C#?

非常感谢你能帮我搞定的任何事情:)

【问题讨论】:

  • 跟问题无关,但是split的过去分词是split,不是split。 :)

标签: c# memory-management garbage-collection out-of-memory


【解决方案1】:

以下两行会杀了你:

int fileSize = (int)FileToSplitInfo.Length; // a FileInfo object
...
byte[] fileBytes = new byte[fileSize];
  1. 当大小超过Int32.MaxValue 时,您的代码将失败。没必要,用long fileSize = FileToSplitInfo.Length;
  2. 当没有足够的连续内存时,这个更正的代码将失败。 (LOH的)碎片化迟早会让你失望。
  3. 您为整个文件分配内存,但一次只需要PieceSize 个字节。

您甚至不需要知道文件大小,只需

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 
}

【讨论】:

    【解决方案2】:

    有很多方面可以做得更好:

    • 如果您正在处理大文件,为什么要先读取数组中的all,然后写入另一个文件?只需在读取另一个文件时写入新文件。

    • 使用using 来保证对流的处理,在任何情况下:要么存在异常,要么不存在异常。

    • 如果您开始使用非常大的文件,例如 1GB 甚至更大,我建议您查看Memory Mapped Files。因此,您将获得令人难以置信的内存消耗优势,同时增加一些性能成本。

    【讨论】:

      猜你喜欢
      • 2013-05-09
      • 1970-01-01
      • 2014-07-15
      • 2011-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多