【问题标题】:StreamReader.ReadToEnd causes massive memory usage / leaksStreamReader.ReadToEnd 导致大量内存使用/泄漏
【发布时间】:2014-03-07 14:00:14
【问题描述】:

它的作用:对于每个 EncryptedBase64PictureFile,读取内容,解密 base64 字符串并创建一个图片框。

问题出在哪里:疯狂的内存使用!我猜每个循环后的一些数据没有被正确删除。例如,输入大约 100MB 的加密数据的 100 个循环,应该生成大约 100MB 的图像文件,使用大约 1.5GB 的内存!当我尝试解密更多数据时,大约 150MB,我得到 OutOfMemory 异常。 Visual Studio 的内存分析报告说,“string fileContent= reader.ReadToEnd();” line 负责 80% 的分配。

for each EncryptedBase64PictureFile {
    Rijndael rijAlg = Rijndael.Create();
    rijAlg.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
    rijAlg.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
    FileStream fsread = new FileStream(EncryptedBase64PictureFile, FileMode.Open, FileAccess.Read);
    ICryptoTransform desdecrypt = rijAlg.CreateDecryptor();
    CryptoStream cryptostreamDecr = new CryptoStream(fsread,desdecrypt, CryptoStreamMode.Read);

    StreamReader reader = new StreamReader(cryptostreamDecr);
    string fileContent= reader.ReadToEnd(); //this should be the memory eater
    var ms = new MemoryStream(Convert.FromBase64String(fileContent));

    PictureBox myPictureBox= new PictureBox();
    myPictureBox.Image = Image.FromStream(ms);

    ms.Close();
    reader.Close();
    cryptostreamDecr.Close();
    fsread.Close();

}

所以问题是,有没有办法在每次循环后正确释放内存?或者是别的什么问题? 感谢每个想法!

编辑: 当然我尝试 dispose() 所有 4 个流,但结果是一样的......

ms.Dispose();
reader.Dispose();
cryptostreamDecr.Dispose();
fsread.Dispose();

编辑: 发现问题。它不是 dispose(),而是从流中创建图片。删除图片后,内存占用从 1.5GB 变为 20MB。

编辑: 图片 .jpg 格式大约 500kb,base64 加密格式大约 700kb。但我真的不知道,imagebox 对象有多大。

编辑: 100 个循环,输入大约 100MB 意味着每个循环大约需要 1MB,100MB 总共是 100 个循环

【问题讨论】:

  • 你有多大的图片?
  • 32 位?它可能只是碎片化。在 32 位内存空间中处理 100mb 块是 - 鲁莽的。这势必会产生问题(大对象堆框架)。
  • 您是否测试过 X 数量的加密数据导致 X 数量的 PictureBox + Image 实例的假设?也许这里有一个大于 1 的因素? (虽然 15 倍听起来确实太多了)
  • 如果图像是 .JPG 图像,那么 100MB 的 JPEG 是很多像素,因此我的原始问题。
  • 为什么要显式调用.Dispose() 没有充分的理由,您真的应该更改代码以使用using 语句。

标签: c# .net memory-management


【解决方案1】:

另一个答案:接受它。

如:您在看似 32 位的应用程序中使用 100mb 块。由于大对象堆和一般内存碎片,如果不重用缓冲区,这将无法工作。

如:内存在那里,只是没有足够大的块。这会导致分配错误。

除了使用 64 位更大的地址空间来处理问题之外,没有真正的解决方法。

相关信息可能在:

https://connect.microsoft.com/VisualStudio/feedback/details/521147/large-object-heap-fragmentation-causes-outofmemoryexception

https://www.simple-talk.com/dotnet/.net-framework/large-object-heap-compaction-should-you-use-it/

最近有一个可能的解决方案,启用大型对象堆压缩:

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(); // This can be omitted

LOH 操作是昂贵的,但是 100mb 的区域运行并不是 GC 推荐的场景。不是 32 位。

【讨论】:

    【解决方案2】:

    解密流时使用 base 64 转换。不要使用Convert.FromBase64String,因为这要求所有数据都在内存中。

    using (FileStream f64 = File.Open(fileout, FileMode.Open) ) // content is in base64
    using (var cs=new CryptoStream(f64, new FromBase64Transform(), CryptoStreamMode.Read ) ) // transform passed to constructor
    using(var fo =File.Open(filein +".orig", FileMode.Create))
    {
        cs.CopyTo(fo); // stream is accessed as if it was already decrypted
    }   
    

    取自此相关答案的代码示例 - Convert a VERY LARGE binary file into a Base64String incrementally

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-07
      • 2017-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-06
      • 2014-06-07
      相关资源
      最近更新 更多