C# 如何在base64转换后释放内存
我正在尝试流式传输文件的内容。 该代码适用于较小的文件,但对于较大的文件,我会出现内存不足错误C# 如何在base64转换后释放内存,c#,memory-leaks,out-of-memory,C#,Memory Leaks,Out Of Memory,我正在尝试流式传输文件的内容。 该代码适用于较小的文件,但对于较大的文件,我会出现内存不足错误 public void StreamEncode(FileStream inputStream, TextWriter tw) { byte[] base64Block = new byte[BLOCK_SIZE]; int bytesRead = 0; try { do { // read one block
public void StreamEncode(FileStream inputStream, TextWriter tw)
{
byte[] base64Block = new byte[BLOCK_SIZE];
int bytesRead = 0;
try
{
do
{
// read one block from the input stream
bytesRead = inputStream.Read(base64Block, 0, base64Block.Length);
// encode the base64 string
string base64String = Convert.ToBase64String(base64Block, 0, bytesRead);
// write the string
tw.Write(base64String);
} while (bytesRead == base64Block.Length);
}
catch (OutOfMemoryException)
{
MessageBox.Show("Error -- Memory used: " + GC.GetTotalMemory(false) + " bytes");
}
}
我可以隔离问题,并观察使用的内存随着循环而增长。问题似乎是调用了
Convert.ToBase64String()
如何释放转换字符串的内存
从这里开始编辑。。。这里有一个更新。 我也创造了一个新的关于这一点-对不起,我想这不是正确的做法 谢谢你的建议。根据建议,我缩小了用于读取文件的缓冲区大小,看起来内存消耗更好,但我仍然看到OOM问题,并且我看到文件大小小到5MB时存在此问题。我可能想处理十倍大的文件 我现在的问题似乎是使用TextWriter 我创建了一个请求,如下所示[通过一些编辑来缩减代码]:
HttpWebRequest oRequest = (HttpWebRequest)WebRequest.Create(new Uri(strURL));
oRequest.Method = httpMethod;
oRequest.ContentType = "application/atom+xml";
oRequest.Headers["Authorization"] = getAuthHeader();
oRequest.ContentLength = strHead.Length + strTail.Length + longContentSize;
oRequest.SendChunked = true;
using (TextWriter tw = new StreamWriter(oRequest.GetRequestStream()))
{
tw.Write(strHead);
using (FileStream fileStream = new FileStream(strPath, FileMode.Open,
FileAccess.Read, System.IO.FileShare.ReadWrite))
{
StreamEncode(fileStream, tw);
}
tw.Write(strTail);
}
.....
这将调用到例程中:
public void StreamEncode(FileStream inputStream, TextWriter tw)
{
// For Base64 there are 4 bytes output for every 3 bytes of input
byte[] base64Block = new byte[9000];
int bytesRead = 0;
string base64String = null;
do
{
// read one block from the input stream
bytesRead = inputStream.Read(base64Block, 0, base64Block.Length);
// encode the base64 string
base64String = Convert.ToBase64String(base64Block, 0, bytesRead);
// write the string
tw.Write(base64String);
} while (bytesRead !=0 );
}
由于潜在的大量内容,我是否应该使用TextWriter以外的东西?创建请求的整个有效负载似乎非常方便
这完全是错误的做法吗?我希望能够支持非常大的文件。尝试将base64String声明从循环中拉出。如果这仍然没有帮助,请尝试在多次迭代后调用垃圾收集器 GC.Collect();
GC.WaitForPendingFinalizers() 尝试将base64String声明从循环中拉出。如果这仍然没有帮助,请尝试在多次迭代后调用垃圾收集器 GC.Collect();
GC.WaitForPendingFinalizers() 尝试减小块大小或避免将Convert调用的结果分配给变量:
bytesRead = inputStream.Read(base64Block, 0, base64Block.Length);
tw.Write(Convert.ToBase64String(base64Block, 0, bytesRead));
尝试减小块大小或避免将Convert调用的结果分配给变量:
bytesRead = inputStream.Read(base64Block, 0, base64Block.Length);
tw.Write(Convert.ToBase64String(base64Block, 0, bytesRead));
如果使用32 kB或更大的
块大小
,则将创建85 kB或更大的字符串,这些字符串在大对象堆上分配。短命对象应该存在于常规堆中,而不是大型对象堆中,因此这可能是内存问题的原因
此外,我发现代码中存在两个潜在问题:
- base64编码在字符串的末尾使用填充,因此,如果将流切分为位并转换为base64字符串,然后将字符串写入流,则不会得到单个base64流
- 检查使用
方法读取的字节数是否与请求的字节数相同不是检查流结束的正确方法。read
方法可以在任何时候读取比请求的字节更少的字节,检查流结尾的正确方法是当该方法返回零时Read
块大小
,则将创建85 kB或更大的字符串,这些字符串分配在大对象堆上。短命对象应该存在于常规堆中,而不是大型对象堆中,因此这可能是内存问题的原因
此外,我发现代码中存在两个潜在问题:
- base64编码在字符串的末尾使用填充,因此,如果将流切分为位并转换为base64字符串,然后将字符串写入流,则不会得到单个base64流
- 检查使用
方法读取的字节数是否与请求的字节数相同不是检查流结束的正确方法。read
方法可以在任何时候读取比请求的字节更少的字节,检查流结尾的正确方法是当该方法返回零时Read
但是,如果您使用的是内存中的TextWriter(如StringWriter),则可能会遇到相同的问题,因为它无法找到足够大的内存块来容纳内部缓冲区。如果您正在写入文件之类的内容,这应该不是问题。请记住,将数据转换为base64时,生成的字符串将长33%(假设输入大小是3的倍数,这在您的情况下可能是个好主意)。如果块大小太大,则可能没有足够的连续内存来容纳生成的base-64字符串 尝试减小块大小,使base-64的每个部分都更小,从而更容易为其分配内存
但是,如果您使用的是内存中的TextWriter(如StringWriter),则可能会遇到相同的问题,因为它无法找到足够大的内存块来容纳内部缓冲区。如果您正在写入文件之类的内容,这应该不是问题。从内存使用的角度来看,代码看起来还可以,但我认为您正在为基于内存的流(如MemoryStream)传递writer,并在其中存储数据会导致异常 如果块大小大于86Kb,则将在大对象堆(LOH)上进行分配,这将改变分配行为,但本身不应导致OOM
注意:您的结束条件不正确-应为bytesRead!=0,在genral Read中,即使剩余数据较多,返回的字节数也可能少于询问的字节数。而且据我所知,FileStream从来没有这样做过。从内存使用的角度来看,代码看起来还可以,但我认为您正在为基于内存的流(如MemoryStream)传递writer,并在其中存储数据会导致异常 If BLOCK_SI