存储数据在.NET DataTable中所需的内存开销是多少?

23

我试图了解与.NET DataTable以及表中的单个DataRow相关的内存开销,即数据表占用多少内存,相比于仅存储每列数据的正确类型的数组所需的内存。
换句话说,数据表比存储每列数据的正确类型的数组所需的内存多多少?
我想会有一些基本的表开销,加上每列的一些量,然后再加上每行的额外量。

那么有人能给出这三种开销中的任何一种或全部的估计值(我想,还需要解释!)吗?

3个回答

23

请记住,DataTable 存储数据的原始版本和更新版本(可能还有其他版本?)。它还具有许多引用,因为它是基于单元格的,对于任何值类型都会进行装箱,很难准确量化内存...

就我个人而言,我很少使用 DataTable - 在我看来,使用类型化的 POCO 类更为明智。但我不会直接使用数组 - List<T>BindingList<T> 更常见。

作为简单的度量标准,您可以创建许多表等,查看内存使用情况;例如,以下显示了一个约为 4.3 倍的因子 - 即比其昂贵 4 倍以上,但这显然在列数、行数、表格数等方面高度依赖于一些因素:

    // takes **roughly** 112Mb  (taskman)
    List<DataTable> tables = new List<DataTable>();
    for (int j = 0; j < 5000; j++)
    {
        DataTable table = new DataTable("foo");
        for (int i = 0; i < 10; i++)
        {
            table.Columns.Add("Col " + i, i % 2 == 0 ? typeof(int)
                                : typeof(string));
        }
        for (int i = 0; i < 100; i++)
        {
            table.Rows.Add(i, "a", i, "b", i, "c", i, "d", i, "e");
        }
        tables.Add(table);
    }
    Console.WriteLine("done");
    Console.ReadLine();

vs

    // takes **roughly** 26Mb (taskman)
    List<List<Foo>> lists = new List<List<Foo>>(5000);
    for (int j = 0; j < 5000; j++)
    {
        List<Foo> list = new List<Foo>(100);
        for (int i = 0; i < 100; i++)
        {
            Foo foo = new Foo { Prop1 = "a", Prop3 = "b",
                 Prop5 = "c", Prop7 = "d", Prop9 = "e"};
            foo.Prop0 = foo.Prop2 = foo.Prop4 = foo.Prop6 = foo.Prop8 = i;
            list.Add(foo);
        }
        lists.Add(list);
    }
    Console.WriteLine("done");
    Console.ReadLine();

(基于)

class Foo
{
    public int Prop0 { get; set; }
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
    public string Prop3 { get; set; }
    public int Prop4 { get; set; }
    public string Prop5 { get; set; }
    public int Prop6 { get; set; }
    public string Prop7 { get; set; }
    public int Prop8 { get; set; }
    public string Prop9 { get; set; }
}

@Marc - 不妨提及AcceptChanges和其他类似方法可用于操作存储的版本库,这并不会造成任何损害。@Nick:底线是,如果你想要轻便简洁,DataX 不是你需要寻找的地方,甚至不需要测量就能得到答案。为什么不编写一些测试来进行衡量呢? - Ruben Bartelink
此外,我很久以前进行了一些分析来测试POCO与DataTable的性能,DataTable填充速度更快,使用的内存比对象集合少,在合理数量的行(10K以上)中。此外,我不确定这一点,但我认为DataTable会涉及大量字符串(我认为是较短的字符串)。 - Pop Catalin
关于“更快填充”的问题,有资格进行分类将会很有趣……对于原始数据,最简单的选项(POCO)应该更快。是否有任何相关数据? - Marc Gravell
我正在准备一些测试,等我有结果了再回复你 :) - Pop Catalin
@Marc - 你显然不能让事情顺其自然 - 我在推动Nick进行测试! :P - Ruben Bartelink
显示剩余2条评论

8

如果您不在列上定义索引,则开销相当低。如果使用字符串缓存,可以获得非常小的内存占用量: 使用HashSet或Dictionary仅使用每个字符串值的1个字符串实例。这听起来很奇怪,但是如果从数据库中获取数据,并且您有多行具有相同的字符串值(例如“ALFKI”),则字符串值相等,但是字符串实例不相等:字符串在内存中存储多次。如果首先使用HashSet过滤重复实例,则在您的数据表中的每个字符串值处有效地使用相同的字符串实例。这可以大大减少内存占用量。当然,如果字符串值已经在某个地方静态定义(因此不是从外部源读取),那么这样做就没有必要了。


.NET的“字符串内联”机制难道不应该为您处理这个问题吗? - anakic
1
@AntonioNakicAlfirevic .NET 不会对 每个 字符串进行内部化 - 只有代码中的字符串字面量或使用 String.Intern 显式内部化的字符串。 - D Stanley

4
这取决于存储的数据量和数据类型。显然,数据越多,占用的内存就越多。DataTable 有一些开销,使其更加昂贵。您还需要注意大对象堆(Large Object Heap)。如果存储的对象超过85kb,该对象将存储在 LOH 中。这可能会对您的垃圾回收造成严重影响,因为它需要进行完整的回收。如果您准备测试,请使用内存分析工具来观察 DataTable 的内存占用情况。

关于字符串的好技巧;仅因此+1 ;-p - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接