正如您似乎暗示的那样,我假设您有一个包含许多成员的类,有大量实例,并且需要同时将它们全部保存在内存中以执行计算。
我进行了一些测试,看看你是否真的可以为你描述的类获得不同的大小。
我使用这个简单的方法来查找对象的内存大小:
private static void MeasureMemory()
{
int size = 10000000;
object[] array = new object[size];
long before = GC.GetTotalMemory(true);
for (int i = 0; i < size; i++)
{
array[i] = new Data();
}
long after = GC.GetTotalMemory(true);
double diff = after - before;
Console.WriteLine("Total bytes: " + diff);
Console.WriteLine("Bytes per object: " + diff / size);
}
它可能很原始,但我发现它适用于这种情况。
正如预期的那样,您对该类几乎无能为力(将其转换为结构、删除继承或方法属性)会影响单个实例使用的内存。就内存使用而言,它们都是等价的。但是,请尝试摆弄您的实际类并通过给定的代码运行它们。
您实际上可以减少实例的内存占用的唯一方法是使用较小的结构来保存您的数据(例如 int 而不是 long)。如果您有大量布尔值,您可以将它们分组为一个字节或整数,并使用简单的属性包装器来处理它们(布尔值占用一个字节的内存)。在大多数情况下,这些可能是微不足道的事情,但对于一亿个对象,删除一个布尔值可能会产生 100 MB 的内存差异。此外,请注意,您为应用程序选择的平台目标可能会影响对象的内存占用(x64 构建占用的内存比 x86 构建的要多)。
序列化数据不太可能有帮助。内存数据库有其优点,尤其是在您进行复杂查询时。但是,实际上不太可能减少数据的内存使用量。不幸的是,没有很多方法可以减少基本数据类型的占用。在某些时候,您只需要移动到基于文件的数据库。
但是,这里有一些想法。请注意,它们是 hacky、高度条件化的,会降低计算性能并且会使代码更难维护。
在大型数据结构中经常出现这样的情况,不同状态的对象只会填充一些属性,而另一些会被设置为 null 或默认值。如果您可以识别此类属性组,也许您可以将它们移动到子类中,并拥有一个可能为空的引用,而不是让多个属性占用空间。然后,您只需在需要时实例化子类。您可以编写可以将其隐藏在其余代码中的属性包装器。请记住,这里最糟糕的情况是将所有属性以及多个对象头和指针保留在内存中。
您也许可以将可能采用默认值的成员转换为二进制表示形式,然后将它们打包成一个字节数组。您将知道哪些字节位置代表哪个数据成员,并且可以编写可以读取它们的属性。将最有可能具有默认值的属性放在字节数组的末尾(例如,一些通常为 0 的长整数)。然后,在创建对象时,调整字节数组大小以排除具有默认值的属性,从列表末尾开始,直到您点击第一个具有非默认值的成员。当外部代码请求一个属性时,您可以检查字节数组是否足够大以容纳该属性,如果没有,则返回默认值。这样,您可能会节省一些空间。最好的情况,您将有一个指向字节数组而不是几个数据成员的空指针。最坏的情况是,全字节数组占用的空间与原始数据一样多,加上数组的一些开销。有用性取决于实际数据,并假设您执行的写入操作相对较少,因为数组的重新计算会很昂贵。
希望这有帮助:)