清理正在使用的内存

时间:2016-12-09 10:37:17

标签: c# asp.net performance memory optimization

我已经使用内存流来下载文件。文件下载后,我需要清除该内存流使用的所有内存。

context.Response.BufferOutput = true;
//DecryptAndStoreInMemory decrypt a requested file and store in memory
using (MemoryStream ms = DecryptAndStoreInMemory(context.Server.MapPath(path), userFileName))
    {
      byte[] bytesInStream = ms.ToArray();
      context.Response.ContentType = "application/octet-stream";
      context.Response.AddHeader("Content-Length", bytesInStream.Length.ToString());
      context.Response.AddHeader("Content-Disposition", "attachment; filename=" + userFileName);
      ClearStream(ms);
      context.Response.BinaryWrite(bytesInStream);
      context.Response.Flush();
      context.Response.Close();
      Array.Clear(bytesInStream, 0, bytesInStream.Length);
    }


ClearStream(MemoryStream stream)
{
    if(stream != null)
    {
        stream.Flush();
        stream.Close();
        stream.Dispose();
    }
}

//解密文件并存储在内存流中。

public MemoryStream DecryptAndStoreInMemory(string inputFilePath, string userFileName)
{
    MemoryStream msOutput = null;
    if (File.Exists(inputFilePath))
    {
        try
        {
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(_password, _salt);
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open))
                {
                    using (CryptoStream cs = new CryptoStream(fsInput, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        using (msOutput = new MemoryStream())
                        {
                            int data;
                            while ((data = cs.ReadByte()) != -1)
                            {
                                msOutput.WriteByte((byte)data);
                            }
                        }
                    }
                }
            }
            return msOutput;
        }
        catch (CryptographicException)
        {
            throw new Exception("Sorry we can not serve " + userFileName + " file at this time.");
        }
        catch (Exception)
        {
            throw;
        }
    }
    else
    {
        throw new Exception("Sorry we could not locate " + userFileName);
    }

}

将文件发送到响应后,它不会释放正在被占用的内存。它几乎占用了文件大小的两倍。 17 MB文件占用近40 MB内存。

应该采取什么措施来释放这些空间。 我试图清除字节数组,但发现它只替换了数组的每个元素,0保持其长度相同。

2 个答案:

答案 0 :(得分:2)

为避免双重缓冲,不要拨打.ToArray() ;相反,您可以通过ms.GetBuffer()访问现有缓冲区。请注意,这是超大的,因此您可能需要使用:

context.Response.BinaryWrite(ms.GetBuffer(), 0, ms.Length);

请注意,您无法确定地告诉数组消失,Array.Clear没有您想要的效果。清理是垃圾收集器的工作,你不应该经常搞乱。

然而!更好的方法是不能一次性读取所有内容,而是使用纯流式方法。我不知道你的DecryptAndStoreInMemory是如何实现的,所以我不知道你的情况是否可行,但是:它通常是。

答案 1 :(得分:1)

在.NET framwerk中,您无法直接控制内存使用情况。您的内存由垃圾收集器(GC)控制。当你处理了你的流并最终被解除引用后,它被称为 dead 。这意味着它可以用于垃圾收集,当内存压力过高时会自动进行垃圾收集。

在绝大多数情况下,这里不需要任何优化,因为仅仅因为使用了内存,并不意味着它在垃圾收集期间无法释放。使用GC的托管内存也不需要比非托管内存慢,因为与非托管内存(如果有足够的可用内存)相比,托管内存中新对象的分配速度很快,因为托管内存未分段。

您可以使用GC.Collect()启动垃圾回收,但这可能会使您的代码更少高性能,而不是自动运行垃圾回收。启动集合不一定会减少已使用的内存大小,因为GC并不总是将释放的内存返回给操作系统。