【问题标题】:putting csv file into memory将csv文件放入内存
【发布时间】:2011-05-03 04:30:48
【问题描述】:

我有一个非常大 (10mb) 的 csv 文件。我解析它并使用通用列表将其放入内存中。

我创建了一个类来表示每一行。这个类只有几个字段(数据类型 ip-address、string)。

我认为由于该文件只有 10 兆字节,因此我可以预期内存中的大小相似。

当我发现创建列表的方法是分配 300 mb 而不是释放它时,我感到非常惊讶。

这是正常的吗?是什么原因造成的。

请注意,csv 文件有很多行 (100 000 +),这可能是一个因素。


命名空间地理 公共类 CountryMarker 公共起始地址作为 IP 地址 公共 EndAddress 作为 IPAddress 公共国家作为字符串 公共 CountryCode 作为字符串 结束类

Public Class Markers
    Private Const DatabasePath = "~/App_Data/ip.csv" '10 MB file
    Public Shared List As List(Of CountryMarker) = LoadData()

    Shared Function LoadData() As List(Of CountryMarker)
        Dim Markers As New List(Of CountryMarker)

        Using Stream = New IO.FileStream(Hosting.HostingEnvironment.MapPath(DatabasePath), FileMode.Open)
            Dim Reader = New StreamReader(Stream)

            Do While Reader.Peek > -1
                Dim Line = Reader.ReadLine()
                Dim Values = Line.Split(",").Select(Function(i) i.Replace("""", ""))

                Markers.Add(New CountryMarker With {.Country = Values(5), .CountryCode = Values(4), .StartAddress = IPAddress.Parse(Values(0)), .EndAddress = IPAddress.Parse(Values(1))})
            Loop
        End Using

        Return Markers
    End Function
End Class

结束命名空间

【问题讨论】:

  • 您想知道为什么会发生这种情况,或者提示另一种加载数据的方式吗?
  • 如果我们看到类的定义会有所帮助。人们也可能更愿意帮助您接受对您之前问题的更多回答。

标签: .net performance


【解决方案1】:

首先,如果文件是 ASCII 文本或 UTF-8,主要是西欧字符(如英语),那么文本的内存大小将至少是磁盘上文件大小的两倍。 .NET 将字符串存储为 16 位 Unicode 值。因此,例如“A”,它在文本文件中占用一个字节,在内存中需要两个字节。

您创建的每个类实例将需要至少 24 个字节(16 个分配字节,加上 8 个用于引用的字节。)如果您的文件是 100,000 行,则最少需要 2.4 兆字节。此外,您分配的每个字符串都需要 24 个字节,加上该字符串所需的任何内容。事情加起来很快。

(请注意,我的 24 字节数是针对 64 位系统的。在 32 位运行时中,每次分配 16 字节。)

正如其他人所评论的,除非您发布一些代码,包括您的类定义,否则不可能为您提供更多细节。

至于不释放任何内存:这有点难以证明。也许垃圾收集器还没有开始收集。如果它没有发现内存压力(即有大量可用内存并且没有其他进程请求内存),GC 可能会决定它不需要收集。

【讨论】:

    【解决方案2】:

    除了 Jim 的评论,如果您将大量项目读入 List,它将在内部以指数级增长的块大小重新分配内存。我不知道确切的启发式方法,但考虑到 .NET 中没有 realloc - 如果您使用 Reflector,您会发现即使 Array.Resize 也会分配一个全新的数组。

    假设您分配了 2049 个对象,并假设 List 在需要更多空间时会将缓冲区大小加倍。你会得到 1、2、4 .. 1024、2048,最后是 4096——几乎是你需要的两倍(这是最坏的情况)。

    一种解决方案是调用 List.TrimExcess()。这将使阵列恢复到合理的大小。更好的解决方案是估计您需要存储多少项目并将其作为初始容量传递给 List 构造函数。

    但是,如果没有看到您的解析器和类的代码,很难说这对您的内存使用问题有多大影响。

    【讨论】:

    • 只是补充一点,如果垃圾收集器有一段时间没有启动,您可能会看到大约 3 倍所需的使用量一段时间。那是因为 2048, 1024, 512 ... 4, 2, 1 缓冲区仍在等待收集。但是,当 GC 启动时,这应该会自行纠正。
    • 列表本身的大小将比所有“线对象”占用更少的空间,即使调整大小会产生多余的空间。
    • 我同意,但是可以有几兆数据以便于挑选。提供容量的成本非常低。
    猜你喜欢
    • 2018-07-05
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 2021-11-20
    相关资源
    最近更新 更多