我使用以下方法在序列化后将应用程序数据保存到文件中,并在反序列化(en / decrypted)后从该文件加载数据。
private void SaveClassToFile(string fileAddress, string password, object classToSave)
{
const int ivSaltLength = 16;
byte[] salt = new byte[ivSaltLength];
byte[] iv = new byte[ivSaltLength];
byte[] codedClass = new byte[0];
iv = CreateIV();
salt = CreateSalt();
using (var memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, classToSave);
codedClass = new byte[Convert.ToInt32(memoryStream.Length)];
memoryStream.Seek(0, SeekOrigin.Begin);
if (memoryStream.Read(codedClass, 0, Convert.ToInt32(memoryStream.Length)) != memoryStream.Length)
{throw new Exception("failed to read from memory stream"); }
}
using (SpecialCoderDecoder specialCoder = new SpecialCoderDecoder(SpecialCoderDecoder.Type.Coder, password, salt, iv))
{ specialCoder.Code(codedClass); }
using (FileStream streamWriter = new FileStream(fileAddress, FileMode.CreateNew))
using (BinaryWriter binaryWriter = new BinaryWriter(streamWriter))
{
binaryWriter.Write(salt);
binaryWriter.Write(iv);
binaryWriter.Write(codedClass);
}
}
private object LoadClassFromFile(string fileAddress, string password)
{
const int ivSaltLength = 16;
byte[] salt = new byte[ivSaltLength];
byte[] iv = new byte[ivSaltLength];
byte[] codedClass = new byte[0];
int codedClassLengthToRaed = 0;
FileInfo fileInfo;
object result = null;
fileInfo = new FileInfo(fileAddress);
using (FileStream streamWriter = new FileStream(fileAddress, FileMode.Open))
using (BinaryReader binaryreader = new BinaryReader(streamWriter))
{
salt = binaryreader.ReadBytes(ivSaltLength);
iv = binaryreader.ReadBytes(ivSaltLength);
codedClassLengthToRaed = Convert.ToInt32(fileInfo.Length) - (2 * ivSaltLength);
codedClass = binaryreader.ReadBytes(codedClassLengthToRaed);
}
using (SpecialCoderDecoder specialDecoder = new SpecialCoderDecoder(SpecialCoderDecoder.Type.Decoder, password, salt, iv))
{ specialDecoder.Decode(codedClass); }
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
memoryStream.Write(codedClass, 0, codedClass.Length);
memoryStream.Seek(0, SeekOrigin.Begin);
result = (object)binaryFormatter.Deserialize(memoryStream);
}
return result;
}
如果应用程序中没有数据,我向它添加大约100MB的数据(基于任务管理器)并保存。加载数据后,任务管理器显示应用程序数据大约为200-400 MB!
为了将应用程序类封装到一个类中以便使用此方法,我使用类似的类:
public class BigClass
{
public ClassA classA;
public ClassB classB;
public BigClass(ClassA a, ClassB b)
{
classA = a;
classB = b;
}
}
每个ClassA
和ClassB
(应该保存/加载的类)都是这样的:
public class ClassA
{
List<ClassASub> list = new List<ClassASub>();
//some variables...
//some methodes
private class ClassASub
{
int intValue;
long longValue;
string stringValue;
Image image;
//some simple methodes....
}
}
我没有谈论序列化/反序列化过程中使用的RAM的大小。在此之后,当只有应用程序数据存在时,我会谈论使用过的RAM。
答案 0 :(得分:4)
您将数据作为数组(codedClass
)加载到内存中。根据你的指示,这个数组可能大约为100MB,更多足以确保它在大对象堆上分配。
现在:GC旨在优化您的整体系统性能;由于多种原因,不旨在不断积极地回收内存:
在你的情况下,你可以尝试使用System.GC
上的方法来强行运行集合,但我认为真正的目标是不分配那些大阵列。如果你可以做任何事情来转移到基于Stream
的模型而不是基于数组的模型,那就太好了。这可能意味着显着改变SpecialCoderDecoder
。
关键点:阵列的上部大小是硬帽;你无法将当前的实现扩展到2GB以上(即使启用了<gcAllowVeryLargeObjects>
)。
此外,我怀疑BinaryFormatter
正在加剧事情 - 它几乎总是如此。存在替代的更有效的陈旧序列化器。减少序列化大小将是另一种选择,可以代替 - 或与 - 结合 - 转移到基于Stream
的模型。
此外:您可以尝试在加密的有效负载中使用压缩技术(GZipStream
,DeflateStream
等) 。您不应尝试压缩加密数据 - 您需要确保订单是:
Serialize -> Compress -> Encrypt -> (storage) -> Decrypt -> Decompress -> Deserialize
序列化和压缩阶段已完全Stream
- 兼容。如果您可以使加密图层Stream
兼容,那么您就是胜利者。
答案 1 :(得分:-1)
您创建的类包含大量数据(例如,A类,B类,BigClass)。 每当你创建和使用包含许多数据的类(特别是值类型)时,你必须告诉运行时当你不再需要它们时销毁它们(或处理它们)。 这被称为&#34; Dispose Pattern&#34;你可以在这里找到更多相关信息:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern
一些.net类内置了Dispose()方法,以便Net Garbage Collector(GC)知道何时将它们擦除内存。但不是全部。 对于那些拥有Dispose()并实现IDisposable接口的人,你可以使用&#34;使用&#34;声明在任务完成时自动处理它们(您在代码中使用了一些using语句,但没有在所有必需的地方使用)。
简单的答案是:序列化完成后,您的数据仍保留在内存中。让你的课程一次性使用,并在你不需要时处理它们。
[这个问题对您有帮助:When should I dispose my objects in .NET?]