StringWriter.ToString() 处的 OutOfMemoryException
OutOfMemoryException at StringWriter.ToString()
在我的 StringWriter 上调用 ToString
时收到 OutOfMemoryException
:
StringWriter stringWriter = new System.IO.StringWriter();
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringWriter, data);
string xmlString = stringWriter.ToString(); // <-- Exception occurs here
我该如何解决这个问题?
我确定 stringWriter 已经非常大了,假设大小超过 1 Gb,并且您的计算机有 2 GB 的总内存。当您调用 ToString () 时,内存将增加一倍,因为将在 1GB 大小的堆中创建和分配一个新字符串,以便能够复制 stringWriter 的内容。尽量避免不必要的 ToString() 并尝试直接使用 stringWriter 来做你需要做的事情。避免使用 ToString(),通常您会减少 50% 的内存占用。
但是如果你真的需要你的数据作为字符串并且你没有足够的内存,请尝试先将内容保存在文件中,处理 StringWriter 并使用 [=28= 将文件内容加载到字符串中] () API.
另一种方法是尝试将序列化数据切开并尝试成块解析。例如,让我们考虑一个具有如下结构的 XML:
<Root>
<Item>some data 1</Item>
<Item>some data 2</Item>
<Item>some data 3</Item>
....
<Item>some data n</Item>
</Root>
您可以将您的对象序列化到 MemoryStream 中,然后以块的形式读取它并从您的块中创建 "little" XML 数据,如下所示:
第一个xml:
<Root>
<Item> some data 1 </Item>
</Root>
第二个xml:
<Root>
<Item> some data 2 </Item>
</Root>
等等,可以单独检查和验证。
这应该可以解决问题:
using (StreamWriter sw = File.AppendText(path))
{
using (StreamReader sr = new StreamReader(ms))
{
while (sr.Peek() >= 0)
{
string l = sr.ReadLine();
sw.WriteLine(l);
}
}
}
试试这个代码。它使用文件作为临时缓冲区。
List<Dummy> lst = new List<Dummy>();
for (var i = 0; i < 100000; i++)
{
lst.Add(new Dummy()
{
X = i,
Y = i * 2
});
}
XmlSerializer serializer = new XmlSerializer(typeof(List<Dummy>));
// estimate your memory consumption ... it would be around 4 bytes reference + 4 bytes object type pointer + 8 bytes those ints + let's say another 4 bytes other hidden CLR metadatas. a total of 20 bytes per instance + 4 bytes reference to our object (in the list array) => around 24 bytes per instance. Round up to a let's say 50 bytes per instance. Multiply it by 100.000 = 5.000.000
MemoryStream memStream = new MemoryStream(5000000);
serializer.Serialize(memStream, lst);
memStream.Position = 0;
string tempDatafileName = null;
var dataWasWritten = false;
try
{
var fileName = Guid.NewGuid().ToString() + ".tempd";
var specialFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
using (var fs = new FileStream(tempDatafileName, FileMode.Create, FileAccess.ReadWrite))
memStream.WriteTo(fs);
dataWasWritten = true;
memStream.Dispose();
memStream = null;
lst.Clear();
lst = null;
// force a full second generational GC
GC.Collect(2);
// reading the content in string
string myXml = File.ReadAllText(tempDatafileName);
}
finally
{
if (dataWasWritten && string.IsNullOrWhiteSpace(tempDatafileName) == false)
{
if (File.Exists(tempDatafileName))
{
try
{
File.Delete(tempDatafileName);
}
catch
{
}
}
}
}
这样做是有效的..
using (MemoryStream ms = new MemoryStream())
{
serializer.Serialize(ms, data);
ms.Position = 0;
XmlReader reader = XmlReader.Create(ms, schemaReaderSettings);
}
在我的 StringWriter 上调用 ToString
时收到 OutOfMemoryException
:
StringWriter stringWriter = new System.IO.StringWriter();
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringWriter, data);
string xmlString = stringWriter.ToString(); // <-- Exception occurs here
我该如何解决这个问题?
我确定 stringWriter 已经非常大了,假设大小超过 1 Gb,并且您的计算机有 2 GB 的总内存。当您调用 ToString () 时,内存将增加一倍,因为将在 1GB 大小的堆中创建和分配一个新字符串,以便能够复制 stringWriter 的内容。尽量避免不必要的 ToString() 并尝试直接使用 stringWriter 来做你需要做的事情。避免使用 ToString(),通常您会减少 50% 的内存占用。
但是如果你真的需要你的数据作为字符串并且你没有足够的内存,请尝试先将内容保存在文件中,处理 StringWriter 并使用 [=28= 将文件内容加载到字符串中] () API.
另一种方法是尝试将序列化数据切开并尝试成块解析。例如,让我们考虑一个具有如下结构的 XML:
<Root>
<Item>some data 1</Item>
<Item>some data 2</Item>
<Item>some data 3</Item>
....
<Item>some data n</Item>
</Root>
您可以将您的对象序列化到 MemoryStream 中,然后以块的形式读取它并从您的块中创建 "little" XML 数据,如下所示:
第一个xml:
<Root>
<Item> some data 1 </Item>
</Root>
第二个xml:
<Root>
<Item> some data 2 </Item>
</Root>
等等,可以单独检查和验证。
这应该可以解决问题:
using (StreamWriter sw = File.AppendText(path))
{
using (StreamReader sr = new StreamReader(ms))
{
while (sr.Peek() >= 0)
{
string l = sr.ReadLine();
sw.WriteLine(l);
}
}
}
试试这个代码。它使用文件作为临时缓冲区。
List<Dummy> lst = new List<Dummy>();
for (var i = 0; i < 100000; i++)
{
lst.Add(new Dummy()
{
X = i,
Y = i * 2
});
}
XmlSerializer serializer = new XmlSerializer(typeof(List<Dummy>));
// estimate your memory consumption ... it would be around 4 bytes reference + 4 bytes object type pointer + 8 bytes those ints + let's say another 4 bytes other hidden CLR metadatas. a total of 20 bytes per instance + 4 bytes reference to our object (in the list array) => around 24 bytes per instance. Round up to a let's say 50 bytes per instance. Multiply it by 100.000 = 5.000.000
MemoryStream memStream = new MemoryStream(5000000);
serializer.Serialize(memStream, lst);
memStream.Position = 0;
string tempDatafileName = null;
var dataWasWritten = false;
try
{
var fileName = Guid.NewGuid().ToString() + ".tempd";
var specialFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
using (var fs = new FileStream(tempDatafileName, FileMode.Create, FileAccess.ReadWrite))
memStream.WriteTo(fs);
dataWasWritten = true;
memStream.Dispose();
memStream = null;
lst.Clear();
lst = null;
// force a full second generational GC
GC.Collect(2);
// reading the content in string
string myXml = File.ReadAllText(tempDatafileName);
}
finally
{
if (dataWasWritten && string.IsNullOrWhiteSpace(tempDatafileName) == false)
{
if (File.Exists(tempDatafileName))
{
try
{
File.Delete(tempDatafileName);
}
catch
{
}
}
}
}
这样做是有效的..
using (MemoryStream ms = new MemoryStream())
{
serializer.Serialize(ms, data);
ms.Position = 0;
XmlReader reader = XmlReader.Create(ms, schemaReaderSettings);
}